terminalhire 0.35.4 → 0.36.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -212,7 +212,7 @@ GitHub (optional enrichment):
212
212
  ## Local profile encryption
213
213
 
214
214
  - Algorithm: **AES-256-GCM** via Node built-in `crypto` (no external deps).
215
- - Key: stored at `~/.terminalhire/key` with `0600` permissions. If `keytar` is installed, the OS keychain is preferred and the key file is not written.
215
+ - Key: stored at `~/.terminalhire/key` with `0600` permissions (a plaintext key file). In a compiled build the OS keychain is **not** used for these stores — the cross-platform plugin ships without `node_modules` and can't reach one. See `docs/adr-002` (keychain promotion is tracked as TERM-9).
216
216
  - Profile file: `~/.terminalhire/profile.enc` — JSON blob `{ iv, tag, ciphertext }` (all hex-encoded).
217
217
  - GitHub token file: `~/.terminalhire/github-token.enc` — same format, same key.
218
218
 
@@ -1,52 +1,12 @@
1
- var __create = Object.create;
2
1
  var __defProp = Object.defineProperty;
3
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
2
  var __getOwnPropNames = Object.getOwnPropertyNames;
5
- var __getProtoOf = Object.getPrototypeOf;
6
- var __hasOwnProp = Object.prototype.hasOwnProperty;
7
3
  var __esm = (fn, res) => function __init() {
8
4
  return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
9
5
  };
10
- var __commonJS = (cb, mod) => function __require() {
11
- return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
12
- };
13
6
  var __export = (target, all) => {
14
7
  for (var name in all)
15
8
  __defProp(target, name, { get: all[name], enumerable: true });
16
9
  };
17
- var __copyProps = (to, from, except, desc) => {
18
- if (from && typeof from === "object" || typeof from === "function") {
19
- for (let key of __getOwnPropNames(from))
20
- if (!__hasOwnProp.call(to, key) && key !== except)
21
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
22
- }
23
- return to;
24
- };
25
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
26
- // If the importer is in node compatibility mode or this is not an ESM
27
- // file that has been converted to a CommonJS file using a Babel-
28
- // compatible transform (i.e. "__esModule" has not been set), then set
29
- // "default" to the CommonJS "module.exports" for node compatibility.
30
- isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
31
- mod
32
- ));
33
-
34
- // ../../node_modules/keytar/lib/keytar.js
35
- var require_keytar = __commonJS({
36
- "../../node_modules/keytar/lib/keytar.js"(exports, module) {
37
- "use strict";
38
- function disabled() {
39
- throw new Error("keytar disabled in this dev checkout (keychain popup guard) \u2014 key-file fallback expected");
40
- }
41
- module.exports = {
42
- getPassword: disabled,
43
- setPassword: disabled,
44
- deletePassword: disabled,
45
- findPassword: disabled,
46
- findCredentials: disabled
47
- };
48
- }
49
- });
50
10
 
51
11
  // src/claims.ts
52
12
  var claims_exports = {};
@@ -259,21 +219,7 @@ var KEY_FILE = join(TERMINALHIRE_DIR, "key");
259
219
  var ALGO = "aes-256-gcm";
260
220
  var KEY_BYTES = 32;
261
221
  var IV_BYTES = 12;
262
- function skipKeychain() {
263
- return process.env.TERMINALHIRE_NO_KEYCHAIN !== void 0 || process.env.CI !== void 0 || process.env.VITEST !== void 0 || process.env.NODE_ENV === "test";
264
- }
265
222
  async function loadKey() {
266
- if (!skipKeychain()) {
267
- try {
268
- const kt = await Promise.resolve().then(() => __toESM(require_keytar(), 1));
269
- const stored = await kt.getPassword("terminalhire", "profile-key");
270
- if (stored) return Buffer.from(stored, "hex");
271
- const key2 = randomBytes(KEY_BYTES);
272
- await kt.setPassword("terminalhire", "profile-key", key2.toString("hex"));
273
- return key2;
274
- } catch {
275
- }
276
- }
277
223
  mkdirSync(TERMINALHIRE_DIR, { recursive: true, mode: 448 });
278
224
  if (existsSync(KEY_FILE)) {
279
225
  return Buffer.from(readFileSync(KEY_FILE, "utf8").trim(), "hex");
@@ -1,36 +1,13 @@
1
1
  #!/usr/bin/env node
2
- var __create = Object.create;
3
2
  var __defProp = Object.defineProperty;
4
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
3
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
- var __getProtoOf = Object.getPrototypeOf;
7
- var __hasOwnProp = Object.prototype.hasOwnProperty;
8
4
  var __esm = (fn, res) => function __init() {
9
5
  return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
10
6
  };
11
- var __commonJS = (cb, mod2) => function __require() {
12
- return mod2 || (0, cb[__getOwnPropNames(cb)[0]])((mod2 = { exports: {} }).exports, mod2), mod2.exports;
13
- };
14
7
  var __export = (target, all) => {
15
8
  for (var name in all)
16
9
  __defProp(target, name, { get: all[name], enumerable: true });
17
10
  };
18
- var __copyProps = (to, from, except, desc) => {
19
- if (from && typeof from === "object" || typeof from === "function") {
20
- for (let key of __getOwnPropNames(from))
21
- if (!__hasOwnProp.call(to, key) && key !== except)
22
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
23
- }
24
- return to;
25
- };
26
- var __toESM = (mod2, isNodeMode, target) => (target = mod2 != null ? __create(__getProtoOf(mod2)) : {}, __copyProps(
27
- // If the importer is in node compatibility mode or this is not an ESM
28
- // file that has been converted to a CommonJS file using a Babel-
29
- // compatible transform (i.e. "__esModule" has not been set), then set
30
- // "default" to the CommonJS "module.exports" for node compatibility.
31
- isNodeMode || !mod2 || !mod2.__esModule ? __defProp(target, "default", { value: mod2, enumerable: true }) : target,
32
- mod2
33
- ));
34
11
 
35
12
  // ../../packages/core/src/types.ts
36
13
  function isBounty(job) {
@@ -1748,6 +1725,32 @@ async function openPRClosingRefs(owner, name, token, signal, governor) {
1748
1725
  return null;
1749
1726
  }
1750
1727
  }
1728
+ async function issueCrossRefPRAttempts(owner, name, issueNumber, token, signal, governor) {
1729
+ const url = `https://api.github.com/repos/${owner}/${name}/issues/${issueNumber}/timeline?per_page=100`;
1730
+ try {
1731
+ const res = governor ? await governor.get(url, { headers: ghHeaders(token), signal }) : await fetch(url, { headers: ghHeaders(token), signal });
1732
+ if (!res || !res.ok) return null;
1733
+ const link = res.headers?.get("link") ?? null;
1734
+ const hasNextPage = link != null && /\brel="next"/.test(link);
1735
+ const events = await res.json();
1736
+ if (!Array.isArray(events)) return null;
1737
+ if (hasNextPage || events.length >= 100) return null;
1738
+ let hasOpenPR = false;
1739
+ let hasClosedPR = false;
1740
+ const prNumbers = [];
1741
+ for (const ev of events) {
1742
+ if (ev?.event !== "cross-referenced") continue;
1743
+ const src = ev.source?.issue;
1744
+ if (!src || src.pull_request == null) continue;
1745
+ if (typeof src.number === "number") prNumbers.push(src.number);
1746
+ if (src.state === "open") hasOpenPR = true;
1747
+ else if (src.state === "closed") hasClosedPR = true;
1748
+ }
1749
+ return { hasOpenPR, hasClosedPR, prNumbers };
1750
+ } catch {
1751
+ return null;
1752
+ }
1753
+ }
1751
1754
  function makeScoringGovernor(governor) {
1752
1755
  return governor ?? makeGitHubGovernor(
1753
1756
  ((url, init) => fetch(url, init)),
@@ -3925,6 +3928,30 @@ var init_feeds = __esm({
3925
3928
  });
3926
3929
 
3927
3930
  // ../../packages/core/src/feeds/contributions.ts
3931
+ function readSearchMaxPages() {
3932
+ const raw = process.env["CONTRIB_SEARCH_MAX_PAGES"];
3933
+ if (raw == null) return DEFAULT_SEARCH_MAX_PAGES;
3934
+ const n = Number.parseInt(raw, 10);
3935
+ if (Number.isNaN(n)) return DEFAULT_SEARCH_MAX_PAGES;
3936
+ return Math.min(Math.max(n, MIN_SEARCH_MAX_PAGES), MAX_SEARCH_MAX_PAGES);
3937
+ }
3938
+ function readMaxContribItems() {
3939
+ const raw = process.env["CONTRIB_MAX_ITEMS"];
3940
+ if (raw == null) return DEFAULT_MAX_CONTRIB_ITEMS;
3941
+ const n = Number.parseInt(raw, 10);
3942
+ if (Number.isNaN(n)) return DEFAULT_MAX_CONTRIB_ITEMS;
3943
+ return Math.min(Math.max(n, MIN_MAX_CONTRIB_ITEMS), MAX_MAX_CONTRIB_ITEMS);
3944
+ }
3945
+ function readMaxContribIssuesScanned() {
3946
+ const raw = process.env["CONTRIB_MAX_ISSUES_SCANNED"];
3947
+ if (raw == null) return DEFAULT_MAX_CONTRIB_ISSUES_SCANNED;
3948
+ const n = Number.parseInt(raw, 10);
3949
+ if (Number.isNaN(n)) return DEFAULT_MAX_CONTRIB_ISSUES_SCANNED;
3950
+ return Math.min(
3951
+ Math.max(n, MIN_MAX_CONTRIB_ISSUES_SCANNED),
3952
+ MAX_MAX_CONTRIB_ISSUES_SCANNED
3953
+ );
3954
+ }
3928
3955
  function authHeaders2() {
3929
3956
  const token = process.env["GITHUB_TOKEN"] ?? process.env["GH_TOKEN"];
3930
3957
  const h = {
@@ -4012,13 +4039,21 @@ async function fetchRateLimit(client) {
4012
4039
  }
4013
4040
  async function searchContribIssues(client, queries) {
4014
4041
  const byUrl = /* @__PURE__ */ new Map();
4042
+ const maxPages = readSearchMaxPages();
4015
4043
  for (const q of queries) {
4016
- const res = await client.json(
4017
- `/search/issues?q=${encodeURIComponent(q)}&sort=created&order=desc&per_page=${SEARCH_PER_PAGE2}`
4018
- );
4019
- for (const it of res?.items ?? []) {
4020
- if (it.pull_request) continue;
4021
- if (!byUrl.has(it.html_url)) byUrl.set(it.html_url, it);
4044
+ for (let page = 1; page <= maxPages; page++) {
4045
+ const res = await client.json(
4046
+ `/search/issues?q=${encodeURIComponent(q)}&sort=created&order=desc&per_page=${SEARCH_PER_PAGE2}&page=${page}`
4047
+ );
4048
+ const items = res?.items;
4049
+ if (items == null) break;
4050
+ for (const it of items) {
4051
+ if (it.pull_request) continue;
4052
+ if (!byUrl.has(it.html_url)) byUrl.set(it.html_url, it);
4053
+ }
4054
+ if (items.length < SEARCH_PER_PAGE2) break;
4055
+ const stats = client.getStats();
4056
+ if (stats.budgetAborted || stats.secondaryAborted) break;
4022
4057
  }
4023
4058
  }
4024
4059
  return [...byUrl.values()].sort(
@@ -4079,10 +4114,14 @@ async function aggregateContributions(opts = {}) {
4079
4114
  probeTimeoutMs: opts.fetchImpl ? null : PROBE_TIMEOUT_MS
4080
4115
  });
4081
4116
  const queries = opts.queries ?? CONTRIB_SEARCH_QUERIES;
4117
+ const maxContribItems = readMaxContribItems();
4082
4118
  const startRl = await fetchRateLimit(client);
4083
4119
  const coreHealthyAtStart = (startRl?.core?.remaining ?? 0) >= 500;
4084
4120
  client.setSecondaryHint(coreHealthyAtStart);
4085
- const issues = (await searchContribIssues(client, queries)).slice(0, MAX_CONTRIB_ISSUES_SCANNED);
4121
+ const issues = (await searchContribIssues(client, queries)).slice(
4122
+ 0,
4123
+ readMaxContribIssuesScanned()
4124
+ );
4086
4125
  const repoCache = /* @__PURE__ */ new Map();
4087
4126
  const contribCache = /* @__PURE__ */ new Map();
4088
4127
  const prRefsCache = /* @__PURE__ */ new Map();
@@ -4172,7 +4211,7 @@ async function aggregateContributions(opts = {}) {
4172
4211
  let contribUndefined = 0;
4173
4212
  let prRefsNull = 0;
4174
4213
  for (const issue of issues) {
4175
- if (jobs.length >= MAX_CONTRIB_ITEMS) break;
4214
+ if (jobs.length >= maxContribItems) break;
4176
4215
  const fullName = repoFullNameFromApiUrl2(issue.repository_url);
4177
4216
  if (!fullName) continue;
4178
4217
  const id = `contribute:${repoKey(fullName)}#${issue.number}`;
@@ -4230,7 +4269,7 @@ async function aggregateContributions(opts = {}) {
4230
4269
  const doDiscovery = opts.discoverRepos ?? (opts.trendingSlugs != null || opts.vocabTerms != null);
4231
4270
  let discoveredEmitted = 0;
4232
4271
  let discoveryBudgetStopped = false;
4233
- if (doDiscovery && jobs.length < MAX_CONTRIB_ITEMS) {
4272
+ if (doDiscovery && jobs.length < maxContribItems) {
4234
4273
  const maxRepos = Math.min(
4235
4274
  Math.max(0, opts.maxDiscoveredRepos ?? MAX_DISCOVERED_REPOS),
4236
4275
  MAX_DISCOVERED_REPOS
@@ -4283,7 +4322,7 @@ async function aggregateContributions(opts = {}) {
4283
4322
  }
4284
4323
  const scanned = candidates.slice(0, maxRepos);
4285
4324
  for (const fullName of scanned) {
4286
- if (jobs.length >= MAX_CONTRIB_ITEMS) break;
4325
+ if (jobs.length >= maxContribItems) break;
4287
4326
  if (client.getStats().budgetAborted || client.getStats().secondaryAborted) {
4288
4327
  discoveryBudgetStopped = true;
4289
4328
  break;
@@ -4308,7 +4347,7 @@ async function aggregateContributions(opts = {}) {
4308
4347
  const repoIssues = (searchRes?.items ?? []).filter((it) => !it.pull_request);
4309
4348
  let perRepoDiscovered = 0;
4310
4349
  for (const issue of repoIssues) {
4311
- if (jobs.length >= MAX_CONTRIB_ITEMS) break;
4350
+ if (jobs.length >= maxContribItems) break;
4312
4351
  if (perRepoDiscovered >= MAX_ISSUES_PER_DISCOVERED_REPO) break;
4313
4352
  if ((perRepo.get(repoKey(fullName)) ?? 0) >= MAX_BOUNTIES_PER_DISCOVERED_REPO) break;
4314
4353
  const id = `contribute:${repoKey(fullName)}#${issue.number}`;
@@ -4373,7 +4412,7 @@ async function aggregateContributions(opts = {}) {
4373
4412
  }
4374
4413
  return jobs;
4375
4414
  }
4376
- var GITHUB_API2, CONTRIB_LABEL_QUERIES, CONTRIB_LANGUAGE_QUERIES, CONTRIB_SEARCH_QUERIES, SEARCH_PER_PAGE2, MAX_CONTRIB_ITEMS, MAX_CONTRIB_ISSUES_SCANNED, MAX_DISCOVERED_REPOS, MAX_ISSUES_PER_DISCOVERED_REPO, DISCOVERY_REPOS_PER_TERM, DISCOVERY_VOCAB_TERMS, DISCOVERY_ISSUE_LABELS, repoKey;
4415
+ var GITHUB_API2, CONTRIB_LABEL_QUERIES, CONTRIB_LANGUAGE_QUERIES, CONTRIB_SEARCH_QUERIES, SEARCH_PER_PAGE2, DEFAULT_SEARCH_MAX_PAGES, MIN_SEARCH_MAX_PAGES, MAX_SEARCH_MAX_PAGES, DEFAULT_MAX_CONTRIB_ITEMS, MIN_MAX_CONTRIB_ITEMS, MAX_MAX_CONTRIB_ITEMS, DEFAULT_MAX_CONTRIB_ISSUES_SCANNED, MIN_MAX_CONTRIB_ISSUES_SCANNED, MAX_MAX_CONTRIB_ISSUES_SCANNED, MAX_DISCOVERED_REPOS, MAX_ISSUES_PER_DISCOVERED_REPO, DISCOVERY_REPOS_PER_TERM, DISCOVERY_VOCAB_TERMS, DISCOVERY_ISSUE_LABELS, repoKey;
4377
4416
  var init_contributions = __esm({
4378
4417
  "../../packages/core/src/feeds/contributions.ts"() {
4379
4418
  "use strict";
@@ -4392,7 +4431,11 @@ var init_contributions = __esm({
4392
4431
  'label:"good-first-issue" type:issue state:open',
4393
4432
  'label:"help wanted" type:issue state:open',
4394
4433
  'label:"help-wanted" type:issue state:open',
4395
- 'label:"up-for-grabs" type:issue state:open'
4434
+ 'label:"up-for-grabs" type:issue state:open',
4435
+ // supply-expansion D: two more first-contribution label families widen the
4436
+ // global newest-first slice WITHOUT relaxing the credential gate.
4437
+ 'label:"beginner-friendly" type:issue state:open',
4438
+ 'label:"first-timers-only" type:issue state:open'
4396
4439
  ];
4397
4440
  CONTRIB_LANGUAGE_QUERIES = [
4398
4441
  ...["rust", "go", "python", "c++", "ruby"].map(
@@ -4400,12 +4443,30 @@ var init_contributions = __esm({
4400
4443
  ),
4401
4444
  ...["rust", "go"].map(
4402
4445
  (lang) => `label:"good first issue" language:${lang} type:issue state:open`
4446
+ ),
4447
+ // supply-expansion D: cover the high-volume web/enterprise ecosystems the
4448
+ // original set omitted. TS/JS were previously left out of "good first issue"
4449
+ // (the global slice over-represented them) but a LANGUAGE-scoped page surfaces
4450
+ // DIFFERENT repos than the global newest-first slice, so re-including them widens
4451
+ // distinct-repo coverage rather than duplicating it.
4452
+ ...["typescript", "javascript", "java", "python"].map(
4453
+ (lang) => `label:"good first issue" language:${lang} type:issue state:open`
4454
+ ),
4455
+ ...["typescript", "javascript", "c#", "php"].map(
4456
+ (lang) => `label:"help wanted" language:${lang} type:issue state:open`
4403
4457
  )
4404
4458
  ];
4405
4459
  CONTRIB_SEARCH_QUERIES = [...CONTRIB_LABEL_QUERIES, ...CONTRIB_LANGUAGE_QUERIES];
4406
4460
  SEARCH_PER_PAGE2 = 100;
4407
- MAX_CONTRIB_ITEMS = 150;
4408
- MAX_CONTRIB_ISSUES_SCANNED = 600;
4461
+ DEFAULT_SEARCH_MAX_PAGES = 1;
4462
+ MIN_SEARCH_MAX_PAGES = 1;
4463
+ MAX_SEARCH_MAX_PAGES = 5;
4464
+ DEFAULT_MAX_CONTRIB_ITEMS = 400;
4465
+ MIN_MAX_CONTRIB_ITEMS = 50;
4466
+ MAX_MAX_CONTRIB_ITEMS = 1e3;
4467
+ DEFAULT_MAX_CONTRIB_ISSUES_SCANNED = 1500;
4468
+ MIN_MAX_CONTRIB_ISSUES_SCANNED = 100;
4469
+ MAX_MAX_CONTRIB_ISSUES_SCANNED = 5e3;
4409
4470
  MAX_DISCOVERED_REPOS = 15;
4410
4471
  MAX_ISSUES_PER_DISCOVERED_REPO = 3;
4411
4472
  DISCOVERY_REPOS_PER_TERM = 20;
@@ -8387,6 +8448,7 @@ __export(src_exports, {
8387
8448
  isOverIntroLimit: () => isOverIntroLimit,
8388
8449
  isTrivialPRTitle: () => isTrivialPRTitle,
8389
8450
  isWinnableIssue: () => isWinnableIssue,
8451
+ issueCrossRefPRAttempts: () => issueCrossRefPRAttempts,
8390
8452
  jobShortToken: () => jobShortToken,
8391
8453
  joinLabels: () => joinLabels,
8392
8454
  labelFor: () => labelFor,
@@ -8458,23 +8520,6 @@ var init_src = __esm({
8458
8520
  }
8459
8521
  });
8460
8522
 
8461
- // ../../node_modules/keytar/lib/keytar.js
8462
- var require_keytar = __commonJS({
8463
- "../../node_modules/keytar/lib/keytar.js"(exports, module) {
8464
- "use strict";
8465
- function disabled() {
8466
- throw new Error("keytar disabled in this dev checkout (keychain popup guard) \u2014 key-file fallback expected");
8467
- }
8468
- module.exports = {
8469
- getPassword: disabled,
8470
- setPassword: disabled,
8471
- deletePassword: disabled,
8472
- findPassword: disabled,
8473
- findCredentials: disabled
8474
- };
8475
- }
8476
- });
8477
-
8478
8523
  // src/crypto-store.ts
8479
8524
  import {
8480
8525
  createCipheriv,
@@ -8519,10 +8564,10 @@ function decrypt(blob, key) {
8519
8564
  function skipKeychain() {
8520
8565
  return process.env.TERMINALHIRE_NO_KEYCHAIN !== void 0 || process.env.CI !== void 0 || process.env.VITEST !== void 0 || process.env.NODE_ENV === "test";
8521
8566
  }
8522
- async function tryLoadFromKeytar(policy) {
8567
+ async function tryLoadFromKeytar() {
8523
8568
  if (forceKeytarUnavailableForTests || skipKeychain()) return null;
8524
8569
  try {
8525
- const kt = policy === "keychain-required" ? createRequire(import.meta.url)("keytar") : await Promise.resolve().then(() => __toESM(require_keytar(), 1));
8570
+ const kt = createRequire(import.meta.url)("keytar");
8526
8571
  const stored = await kt.getPassword(KEYTAR_SERVICE, KEYTAR_ACCOUNT);
8527
8572
  if (stored) {
8528
8573
  return Buffer.from(stored, "hex");
@@ -8563,7 +8608,7 @@ async function deleteKey() {
8563
8608
  }
8564
8609
  if (!forceKeytarUnavailableForTests && !skipKeychain()) {
8565
8610
  try {
8566
- const kt = await Promise.resolve().then(() => __toESM(require_keytar(), 1));
8611
+ const kt = createRequire(import.meta.url)("keytar");
8567
8612
  await kt.deletePassword(KEYTAR_SERVICE, KEYTAR_ACCOUNT);
8568
8613
  } catch {
8569
8614
  }
@@ -8575,15 +8620,13 @@ async function deleteKey() {
8575
8620
  }
8576
8621
  async function resolveKey(filePath, opts) {
8577
8622
  if (opts.keyPolicy === "keychain-required") {
8578
- const key = await tryLoadFromKeytar("keychain-required");
8623
+ const key = await tryLoadFromKeytar();
8579
8624
  if (!key) {
8580
8625
  warnStderr(`crypto-store: OS keychain unavailable \u2014 store at ${filePath} is disabled (no plaintext key file will be written)`);
8581
8626
  return null;
8582
8627
  }
8583
8628
  return key;
8584
8629
  }
8585
- const fromKeytar = await tryLoadFromKeytar("keytar-first-file-fallback");
8586
- if (fromKeytar) return fromKeytar;
8587
8630
  return loadOrCreateFileKey();
8588
8631
  }
8589
8632
  function createEncryptedStore(filePath, opts) {
@@ -1,32 +1,8 @@
1
1
  #!/usr/bin/env node
2
- var __create = Object.create;
3
- var __defProp = Object.defineProperty;
4
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
2
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
- var __getProtoOf = Object.getPrototypeOf;
7
- var __hasOwnProp = Object.prototype.hasOwnProperty;
8
3
  var __esm = (fn, res) => function __init() {
9
4
  return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
10
5
  };
11
- var __commonJS = (cb, mod2) => function __require() {
12
- return mod2 || (0, cb[__getOwnPropNames(cb)[0]])((mod2 = { exports: {} }).exports, mod2), mod2.exports;
13
- };
14
- var __copyProps = (to, from, except, desc) => {
15
- if (from && typeof from === "object" || typeof from === "function") {
16
- for (let key of __getOwnPropNames(from))
17
- if (!__hasOwnProp.call(to, key) && key !== except)
18
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
19
- }
20
- return to;
21
- };
22
- var __toESM = (mod2, isNodeMode, target) => (target = mod2 != null ? __create(__getProtoOf(mod2)) : {}, __copyProps(
23
- // If the importer is in node compatibility mode or this is not an ESM
24
- // file that has been converted to a CommonJS file using a Babel-
25
- // compatible transform (i.e. "__esModule" has not been set), then set
26
- // "default" to the CommonJS "module.exports" for node compatibility.
27
- isNodeMode || !mod2 || !mod2.__esModule ? __defProp(target, "default", { value: mod2, enumerable: true }) : target,
28
- mod2
29
- ));
30
6
 
31
7
  // ../../packages/core/src/types.ts
32
8
  var init_types = __esm({
@@ -892,7 +868,11 @@ var init_contributions = __esm({
892
868
  'label:"good-first-issue" type:issue state:open',
893
869
  'label:"help wanted" type:issue state:open',
894
870
  'label:"help-wanted" type:issue state:open',
895
- 'label:"up-for-grabs" type:issue state:open'
871
+ 'label:"up-for-grabs" type:issue state:open',
872
+ // supply-expansion D: two more first-contribution label families widen the
873
+ // global newest-first slice WITHOUT relaxing the credential gate.
874
+ 'label:"beginner-friendly" type:issue state:open',
875
+ 'label:"first-timers-only" type:issue state:open'
896
876
  ];
897
877
  CONTRIB_LANGUAGE_QUERIES = [
898
878
  ...["rust", "go", "python", "c++", "ruby"].map(
@@ -900,6 +880,17 @@ var init_contributions = __esm({
900
880
  ),
901
881
  ...["rust", "go"].map(
902
882
  (lang) => `label:"good first issue" language:${lang} type:issue state:open`
883
+ ),
884
+ // supply-expansion D: cover the high-volume web/enterprise ecosystems the
885
+ // original set omitted. TS/JS were previously left out of "good first issue"
886
+ // (the global slice over-represented them) but a LANGUAGE-scoped page surfaces
887
+ // DIFFERENT repos than the global newest-first slice, so re-including them widens
888
+ // distinct-repo coverage rather than duplicating it.
889
+ ...["typescript", "javascript", "java", "python"].map(
890
+ (lang) => `label:"good first issue" language:${lang} type:issue state:open`
891
+ ),
892
+ ...["typescript", "javascript", "c#", "php"].map(
893
+ (lang) => `label:"help wanted" language:${lang} type:issue state:open`
903
894
  )
904
895
  ];
905
896
  CONTRIB_SEARCH_QUERIES = [...CONTRIB_LABEL_QUERIES, ...CONTRIB_LANGUAGE_QUERIES];
@@ -4107,23 +4098,6 @@ var init_src = __esm({
4107
4098
  }
4108
4099
  });
4109
4100
 
4110
- // ../../node_modules/keytar/lib/keytar.js
4111
- var require_keytar = __commonJS({
4112
- "../../node_modules/keytar/lib/keytar.js"(exports, module) {
4113
- "use strict";
4114
- function disabled() {
4115
- throw new Error("keytar disabled in this dev checkout (keychain popup guard) \u2014 key-file fallback expected");
4116
- }
4117
- module.exports = {
4118
- getPassword: disabled,
4119
- setPassword: disabled,
4120
- deletePassword: disabled,
4121
- findPassword: disabled,
4122
- findCredentials: disabled
4123
- };
4124
- }
4125
- });
4126
-
4127
4101
  // src/github-auth.ts
4128
4102
  import {
4129
4103
  createCipheriv,
@@ -4140,21 +4114,7 @@ import {
4140
4114
  } from "fs";
4141
4115
  import { join as join2 } from "path";
4142
4116
  import { homedir } from "os";
4143
- function skipKeychain() {
4144
- return process.env.TERMINALHIRE_NO_KEYCHAIN !== void 0 || process.env.CI !== void 0 || process.env.VITEST !== void 0 || process.env.NODE_ENV === "test";
4145
- }
4146
4117
  async function loadKey() {
4147
- if (!skipKeychain()) {
4148
- try {
4149
- const kt = await Promise.resolve().then(() => __toESM(require_keytar(), 1));
4150
- const stored = await kt.getPassword("terminalhire", "profile-key");
4151
- if (stored) return Buffer.from(stored, "hex");
4152
- const key2 = randomBytes3(KEY_BYTES);
4153
- await kt.setPassword("terminalhire", "profile-key", key2.toString("hex"));
4154
- return key2;
4155
- } catch {
4156
- }
4157
- }
4158
4118
  mkdirSync(TERMINALHIRE_DIR, { recursive: true, mode: 448 });
4159
4119
  if (existsSync(KEY_FILE)) {
4160
4120
  return Buffer.from(readFileSync2(KEY_FILE, "utf8").trim(), "hex");
@@ -1,36 +1,13 @@
1
1
  #!/usr/bin/env node
2
- var __create = Object.create;
3
2
  var __defProp = Object.defineProperty;
4
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
3
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
- var __getProtoOf = Object.getPrototypeOf;
7
- var __hasOwnProp = Object.prototype.hasOwnProperty;
8
4
  var __esm = (fn, res) => function __init() {
9
5
  return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
10
6
  };
11
- var __commonJS = (cb, mod2) => function __require() {
12
- return mod2 || (0, cb[__getOwnPropNames(cb)[0]])((mod2 = { exports: {} }).exports, mod2), mod2.exports;
13
- };
14
7
  var __export = (target, all) => {
15
8
  for (var name in all)
16
9
  __defProp(target, name, { get: all[name], enumerable: true });
17
10
  };
18
- var __copyProps = (to, from, except, desc) => {
19
- if (from && typeof from === "object" || typeof from === "function") {
20
- for (let key of __getOwnPropNames(from))
21
- if (!__hasOwnProp.call(to, key) && key !== except)
22
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
23
- }
24
- return to;
25
- };
26
- var __toESM = (mod2, isNodeMode, target) => (target = mod2 != null ? __create(__getProtoOf(mod2)) : {}, __copyProps(
27
- // If the importer is in node compatibility mode or this is not an ESM
28
- // file that has been converted to a CommonJS file using a Babel-
29
- // compatible transform (i.e. "__esModule" has not been set), then set
30
- // "default" to the CommonJS "module.exports" for node compatibility.
31
- isNodeMode || !mod2 || !mod2.__esModule ? __defProp(target, "default", { value: mod2, enumerable: true }) : target,
32
- mod2
33
- ));
34
11
 
35
12
  // ../../packages/core/src/types.ts
36
13
  var init_types = __esm({
@@ -909,7 +886,11 @@ var init_contributions = __esm({
909
886
  'label:"good-first-issue" type:issue state:open',
910
887
  'label:"help wanted" type:issue state:open',
911
888
  'label:"help-wanted" type:issue state:open',
912
- 'label:"up-for-grabs" type:issue state:open'
889
+ 'label:"up-for-grabs" type:issue state:open',
890
+ // supply-expansion D: two more first-contribution label families widen the
891
+ // global newest-first slice WITHOUT relaxing the credential gate.
892
+ 'label:"beginner-friendly" type:issue state:open',
893
+ 'label:"first-timers-only" type:issue state:open'
913
894
  ];
914
895
  CONTRIB_LANGUAGE_QUERIES = [
915
896
  ...["rust", "go", "python", "c++", "ruby"].map(
@@ -917,6 +898,17 @@ var init_contributions = __esm({
917
898
  ),
918
899
  ...["rust", "go"].map(
919
900
  (lang) => `label:"good first issue" language:${lang} type:issue state:open`
901
+ ),
902
+ // supply-expansion D: cover the high-volume web/enterprise ecosystems the
903
+ // original set omitted. TS/JS were previously left out of "good first issue"
904
+ // (the global slice over-represented them) but a LANGUAGE-scoped page surfaces
905
+ // DIFFERENT repos than the global newest-first slice, so re-including them widens
906
+ // distinct-repo coverage rather than duplicating it.
907
+ ...["typescript", "javascript", "java", "python"].map(
908
+ (lang) => `label:"good first issue" language:${lang} type:issue state:open`
909
+ ),
910
+ ...["typescript", "javascript", "c#", "php"].map(
911
+ (lang) => `label:"help wanted" language:${lang} type:issue state:open`
920
912
  )
921
913
  ];
922
914
  CONTRIB_SEARCH_QUERIES = [...CONTRIB_LABEL_QUERIES, ...CONTRIB_LANGUAGE_QUERIES];
@@ -4135,23 +4127,6 @@ var init_src = __esm({
4135
4127
  }
4136
4128
  });
4137
4129
 
4138
- // ../../node_modules/keytar/lib/keytar.js
4139
- var require_keytar = __commonJS({
4140
- "../../node_modules/keytar/lib/keytar.js"(exports, module) {
4141
- "use strict";
4142
- function disabled() {
4143
- throw new Error("keytar disabled in this dev checkout (keychain popup guard) \u2014 key-file fallback expected");
4144
- }
4145
- module.exports = {
4146
- getPassword: disabled,
4147
- setPassword: disabled,
4148
- deletePassword: disabled,
4149
- findPassword: disabled,
4150
- findCredentials: disabled
4151
- };
4152
- }
4153
- });
4154
-
4155
4130
  // src/github-auth.ts
4156
4131
  import {
4157
4132
  createCipheriv,
@@ -4168,21 +4143,7 @@ import {
4168
4143
  } from "fs";
4169
4144
  import { join as join2 } from "path";
4170
4145
  import { homedir } from "os";
4171
- function skipKeychain() {
4172
- return process.env.TERMINALHIRE_NO_KEYCHAIN !== void 0 || process.env.CI !== void 0 || process.env.VITEST !== void 0 || process.env.NODE_ENV === "test";
4173
- }
4174
4146
  async function loadKey() {
4175
- if (!skipKeychain()) {
4176
- try {
4177
- const kt = await Promise.resolve().then(() => __toESM(require_keytar(), 1));
4178
- const stored = await kt.getPassword("terminalhire", "profile-key");
4179
- if (stored) return Buffer.from(stored, "hex");
4180
- const key2 = randomBytes3(KEY_BYTES);
4181
- await kt.setPassword("terminalhire", "profile-key", key2.toString("hex"));
4182
- return key2;
4183
- } catch {
4184
- }
4185
- }
4186
4147
  mkdirSync(TERMINALHIRE_DIR, { recursive: true, mode: 448 });
4187
4148
  if (existsSync(KEY_FILE)) {
4188
4149
  return Buffer.from(readFileSync2(KEY_FILE, "utf8").trim(), "hex");
@@ -5072,13 +5033,13 @@ function decrypt2(blob, key) {
5072
5033
  ]);
5073
5034
  return plain.toString("utf8");
5074
5035
  }
5075
- function skipKeychain2() {
5036
+ function skipKeychain() {
5076
5037
  return process.env.TERMINALHIRE_NO_KEYCHAIN !== void 0 || process.env.CI !== void 0 || process.env.VITEST !== void 0 || process.env.NODE_ENV === "test";
5077
5038
  }
5078
- async function tryLoadFromKeytar(policy) {
5079
- if (forceKeytarUnavailableForTests || skipKeychain2()) return null;
5039
+ async function tryLoadFromKeytar() {
5040
+ if (forceKeytarUnavailableForTests || skipKeychain()) return null;
5080
5041
  try {
5081
- const kt = policy === "keychain-required" ? createRequire(import.meta.url)("keytar") : await Promise.resolve().then(() => __toESM(require_keytar(), 1));
5042
+ const kt = createRequire(import.meta.url)("keytar");
5082
5043
  const stored = await kt.getPassword(KEYTAR_SERVICE, KEYTAR_ACCOUNT);
5083
5044
  if (stored) {
5084
5045
  return Buffer.from(stored, "hex");
@@ -5117,9 +5078,9 @@ async function deleteKey() {
5117
5078
  } catch {
5118
5079
  }
5119
5080
  }
5120
- if (!forceKeytarUnavailableForTests && !skipKeychain2()) {
5081
+ if (!forceKeytarUnavailableForTests && !skipKeychain()) {
5121
5082
  try {
5122
- const kt = await Promise.resolve().then(() => __toESM(require_keytar(), 1));
5083
+ const kt = createRequire(import.meta.url)("keytar");
5123
5084
  await kt.deletePassword(KEYTAR_SERVICE, KEYTAR_ACCOUNT);
5124
5085
  } catch {
5125
5086
  }
@@ -5131,15 +5092,13 @@ async function deleteKey() {
5131
5092
  }
5132
5093
  async function resolveKey(filePath, opts) {
5133
5094
  if (opts.keyPolicy === "keychain-required") {
5134
- const key = await tryLoadFromKeytar("keychain-required");
5095
+ const key = await tryLoadFromKeytar();
5135
5096
  if (!key) {
5136
5097
  warnStderr(`crypto-store: OS keychain unavailable \u2014 store at ${filePath} is disabled (no plaintext key file will be written)`);
5137
5098
  return null;
5138
5099
  }
5139
5100
  return key;
5140
5101
  }
5141
- const fromKeytar = await tryLoadFromKeytar("keytar-first-file-fallback");
5142
- if (fromKeytar) return fromKeytar;
5143
5102
  return loadOrCreateFileKey();
5144
5103
  }
5145
5104
  function createEncryptedStore(filePath, opts) {