hunter-mate 0.1.25 → 0.1.26

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 (80) hide show
  1. package/dist/assets/extension/background/{service-worker.20261017183328.js → service-worker.20261019161523.js} +56 -4
  2. package/dist/assets/extension/background/service-worker.js +56 -4
  3. package/dist/assets/extension/build-info.json +1 -1
  4. package/dist/assets/extension/manifest.json +3 -3
  5. package/dist/assets/server/main.mjs +885 -782
  6. package/dist/index.js +20 -5
  7. package/dist/watchdog.js +14 -3
  8. package/dist/web/dist/404.html +1 -1
  9. package/dist/web/dist/_next/static/chunks/187-f067914dcd1330c2.js +32 -0
  10. package/dist/web/dist/_next/static/chunks/253-9278ce6c4d446329.js +1 -0
  11. package/dist/web/dist/_next/static/chunks/{589-f984bff035a1cb85.js → 589-cc56950ae4117cdd.js} +1 -1
  12. package/dist/web/dist/_next/static/chunks/659-122057f46f941df7.js +1 -0
  13. package/dist/web/dist/_next/static/chunks/{686-9f419b70feea9470.js → 686-05b99d3674f45bf7.js} +1 -1
  14. package/dist/web/dist/_next/static/chunks/728-cffab7e836020551.js +12 -0
  15. package/dist/web/dist/_next/static/chunks/{865-0ed23dd43c0832a2.js → 865-8e1d8c34192526ed.js} +1 -1
  16. package/dist/web/dist/_next/static/chunks/{935-7be3e60dd8bc29d2.js → 935-efb30b9f6f558e3f.js} +1 -1
  17. package/dist/web/dist/_next/static/chunks/{a7067448-e416655baf443992.js → a7067448-3f2b194cfadb91fb.js} +1 -1
  18. package/dist/web/dist/_next/static/chunks/app/_not-found/{page-5b7249eb3c9a6cb9.js → page-d939b40bb8878d26.js} +1 -1
  19. package/dist/web/dist/_next/static/chunks/app/admin/data/{page-14698e9c4ddaa6d9.js → page-df1d84c14e85f294.js} +1 -1
  20. package/dist/web/dist/_next/static/chunks/app/auth/verify/{page-05304d5803008d61.js → page-cfeb844e1b72ccaa.js} +1 -1
  21. package/dist/web/dist/_next/static/chunks/app/companies/{page-e8d02fe997579315.js → page-57b1ef0b12134ce0.js} +1 -1
  22. package/dist/web/dist/_next/static/chunks/app/company/admin/{page-c2584183bc4b34d1.js → page-010928306f8df0ed.js} +1 -1
  23. package/dist/web/dist/_next/static/chunks/app/entity/{page-08755f96102f0565.js → page-fa96319650d38e78.js} +1 -1
  24. package/dist/web/dist/_next/static/chunks/app/jobs/page-5ba0bdc881b98d42.js +1 -0
  25. package/dist/web/dist/_next/static/chunks/app/{layout-af39afc7866e74db.js → layout-bd9a7e5e4474b692.js} +1 -1
  26. package/dist/web/dist/_next/static/chunks/app/logs/{page-be50a4e10b1a0024.js → page-bcdbb3885abda5a2.js} +1 -1
  27. package/dist/web/dist/_next/static/chunks/app/matching/jobs/[id]/talents/{page-cf445b0cb8c2cb0a.js → page-8aca89db1e666a49.js} +1 -1
  28. package/dist/web/dist/_next/static/chunks/app/matching/portraits/[id]/jobs/{page-c7ff0d79b6a75915.js → page-7086f1b773e57511.js} +1 -1
  29. package/dist/web/dist/_next/static/chunks/app/{page-bd282e1e5f0965f1.js → page-49fe1e8630d2237e.js} +1 -1
  30. package/dist/web/dist/_next/static/chunks/app/parselog/{page-9814e75d6900a743.js → page-fcdfdd41cf1f86f7.js} +1 -1
  31. package/dist/web/dist/_next/static/chunks/app/recordings/{page-441016638b43c4f8.js → page-7ba5d89391c9dace.js} +1 -1
  32. package/dist/web/dist/_next/static/chunks/app/resource-usage/{page-f6fa06bfe21bcd32.js → page-7b939cf673d764e6.js} +1 -1
  33. package/dist/web/dist/_next/static/chunks/app/shared/[token]/{page-c9c5042a2d07c0bc.js → page-df87328c52f862f1.js} +1 -1
  34. package/dist/web/dist/_next/static/chunks/app/snapshots/[id]/agent-prompt/{page-f54cf2f0058035ca.js → page-7d4d25de2c6704e9.js} +1 -1
  35. package/dist/web/dist/_next/static/chunks/app/talents/{page-0edef6d07b3cf69d.js → page-5a2caa7a503ee041.js} +1 -1
  36. package/dist/web/dist/_next/static/chunks/app/team/insights/{page-8b3934df3e292368.js → page-f097b4b8578a6e5d.js} +1 -1
  37. package/dist/web/dist/_next/static/css/a8f0a381a2372781.css +3 -0
  38. package/dist/web/dist/admin/data.html +1 -1
  39. package/dist/web/dist/admin/data.txt +5 -5
  40. package/dist/web/dist/auth/verify.html +1 -1
  41. package/dist/web/dist/auth/verify.txt +5 -5
  42. package/dist/web/dist/companies.html +1 -1
  43. package/dist/web/dist/companies.txt +5 -5
  44. package/dist/web/dist/company/admin.html +1 -1
  45. package/dist/web/dist/company/admin.txt +5 -5
  46. package/dist/web/dist/entity.html +1 -1
  47. package/dist/web/dist/entity.txt +5 -5
  48. package/dist/web/dist/index.html +1 -1
  49. package/dist/web/dist/index.txt +5 -5
  50. package/dist/web/dist/jobs.html +1 -1
  51. package/dist/web/dist/jobs.txt +5 -5
  52. package/dist/web/dist/logs.html +1 -1
  53. package/dist/web/dist/logs.txt +5 -5
  54. package/dist/web/dist/matching/jobs/placeholder/talents.html +1 -1
  55. package/dist/web/dist/matching/jobs/placeholder/talents.txt +5 -5
  56. package/dist/web/dist/matching/portraits/placeholder/jobs.html +1 -1
  57. package/dist/web/dist/matching/portraits/placeholder/jobs.txt +5 -5
  58. package/dist/web/dist/parselog.html +1 -1
  59. package/dist/web/dist/parselog.txt +5 -5
  60. package/dist/web/dist/recordings.html +1 -1
  61. package/dist/web/dist/recordings.txt +5 -5
  62. package/dist/web/dist/resource-usage.html +1 -1
  63. package/dist/web/dist/resource-usage.txt +5 -5
  64. package/dist/web/dist/shared/placeholder.html +1 -1
  65. package/dist/web/dist/shared/placeholder.txt +5 -5
  66. package/dist/web/dist/snapshots/placeholder/agent-prompt.html +1 -1
  67. package/dist/web/dist/snapshots/placeholder/agent-prompt.txt +5 -5
  68. package/dist/web/dist/talents.html +1 -1
  69. package/dist/web/dist/talents.txt +5 -5
  70. package/dist/web/dist/team/insights.html +1 -1
  71. package/dist/web/dist/team/insights.txt +5 -5
  72. package/package.json +1 -1
  73. package/dist/web/dist/_next/static/chunks/105-dcd8f6d5867c8995.js +0 -32
  74. package/dist/web/dist/_next/static/chunks/253-a05d05b94bfbd7a4.js +0 -1
  75. package/dist/web/dist/_next/static/chunks/555-e2a871b9d2eccb03.js +0 -12
  76. package/dist/web/dist/_next/static/chunks/852-ad19b97821a4cf9e.js +0 -1
  77. package/dist/web/dist/_next/static/chunks/app/jobs/page-4827a9ef286b6483.js +0 -1
  78. package/dist/web/dist/_next/static/css/10ba1fa1d6ce23a7.css +0 -3
  79. /package/dist/web/dist/_next/static/{5MA8faUttk6FzlxGWnOlM → m0UGHVTGvtvb9mn4-uzJJ}/_buildManifest.js +0 -0
  80. /package/dist/web/dist/_next/static/{5MA8faUttk6FzlxGWnOlM → m0UGHVTGvtvb9mn4-uzJJ}/_ssgManifest.js +0 -0
@@ -2339,6 +2339,31 @@ async function findDetailTabToReuse(url) {
2339
2339
  return idle?.id;
2340
2340
  }
2341
2341
 
2342
+ // src/background/linkedin-search-filter.ts
2343
+ var LINKEDIN_DEFAULT_LOAD_TIMEOUT_MS = 9e4;
2344
+ var MIN_TIMEOUT_MS = 5e3;
2345
+ var MAX_TIMEOUT_MS = 3e5;
2346
+ function clampLoadTimeoutMs(raw) {
2347
+ const n = Number(raw);
2348
+ if (!Number.isFinite(n) || n <= 0) return LINKEDIN_DEFAULT_LOAD_TIMEOUT_MS;
2349
+ return Math.min(Math.max(n, MIN_TIMEOUT_MS), MAX_TIMEOUT_MS);
2350
+ }
2351
+ function buildCardMatcher(filters) {
2352
+ const needle = (v) => typeof v === "string" && v.trim() ? v.trim().toLowerCase() : null;
2353
+ const locNeedle = filters.geoUrn ? null : needle(filters.location);
2354
+ const coNeedle = needle(filters.company);
2355
+ return {
2356
+ locNeedle,
2357
+ coNeedle,
2358
+ match(ctx) {
2359
+ const h = (ctx || "").toLowerCase();
2360
+ if (locNeedle && !h.includes(locNeedle)) return false;
2361
+ if (coNeedle && !h.includes(coNeedle)) return false;
2362
+ return true;
2363
+ }
2364
+ };
2365
+ }
2366
+
2342
2367
  // src/background/control.ts
2343
2368
  var RUNTIME_BRIDGE_FILE = "content/control-runtime.js";
2344
2369
  async function invokeRuntimeBridge(command, args) {
@@ -2940,12 +2965,25 @@ var LINKEDIN_EXTRACT_SOURCE = String.raw`(async () => {
2940
2965
  });
2941
2966
  } else if (pageClass === 'list') {
2942
2967
  const seen = new Set();
2968
+ // 卡片上下文:LinkedIn list 页 a[href*=/in/] 埋在结果卡内,向上找最小"像卡片"的祖先
2969
+ // (innerText 40-500 字符),抓 headline/地区/公司行——location/company 过滤的判定依据
2970
+ // (2026-10-18 寻访实测:LinkedIn 对自由文本 location/company 查询参数基本不生效,
2971
+ // 必须在 client 端按卡片文本过滤,否则过滤器名存实亡)。
2972
+ const cardCtx = (a) => {
2973
+ let el = a;
2974
+ for (let up = 0; up < 5 && el.parentElement; up++) {
2975
+ el = el.parentElement;
2976
+ const t = (el.innerText || '').replace(/\s+/g, ' ').trim();
2977
+ if (t.length >= 40) return t.slice(0, 400);
2978
+ }
2979
+ return null;
2980
+ };
2943
2981
  for (const a of Array.from(document.querySelectorAll('a[href]'))) {
2944
2982
  const href = a.href || '';
2945
2983
  let m = href.match(/linkedin\.com\/in\/([^/?#]+)/);
2946
2984
  if (m && !seen.has('t:' + m[1])) {
2947
2985
  seen.add('t:' + m[1]);
2948
- links.talent.push({ url: 'https://www.linkedin.com/in/' + m[1] + '/', name: (a.textContent || '').trim().split('\n')[0] || m[1] });
2986
+ links.talent.push({ url: 'https://www.linkedin.com/in/' + m[1] + '/', name: (a.textContent || '').trim().split('\n')[0] || m[1], ctx: cardCtx(a) });
2949
2987
  }
2950
2988
  m = href.match(/linkedin\.com\/company\/([^/?#]+)/);
2951
2989
  if (m && !seen.has('c:' + m[1])) {
@@ -3025,7 +3063,7 @@ async function runLinkedInSearch(args, log, ctx) {
3025
3063
  const maxPages = Math.min(Math.max(Number(args.maxPages) || 3, 1), 10);
3026
3064
  const keepTab = args.keepTab === true;
3027
3065
  const activateTab = args.activateTab !== false;
3028
- const loadTimeoutMs = Math.min(Math.max(Number(args.loadTimeoutMs) || 25e3, 5e3), 3e5);
3066
+ const loadTimeoutMs = clampLoadTimeoutMs(args.loadTimeoutMs);
3029
3067
  const delayBase = typeof args.delayMs === "number" ? Number(args.delayMs) : 2500;
3030
3068
  let effectiveKeywords = keywords;
3031
3069
  if (!filters.geoUrn && typeof filters.location === "string" && filters.location.trim()) {
@@ -3039,6 +3077,9 @@ async function runLinkedInSearch(args, log, ctx) {
3039
3077
  if (typeof filters.geoUrn === "string" && filters.geoUrn) u.searchParams.set("geoUrn", filters.geoUrn);
3040
3078
  if (typeof filters.company === "string" && filters.company) u.searchParams.set("company", filters.company);
3041
3079
  if (typeof filters.school === "string" && filters.school) u.searchParams.set("school", filters.school);
3080
+ const matcher = buildCardMatcher(filters);
3081
+ const locNeedle = matcher.locNeedle;
3082
+ const coNeedle = matcher.coNeedle;
3042
3083
  const tab = await chrome.tabs.create({ url: u.toString(), active: false });
3043
3084
  log("info", "hm.linkedin.search tab created", { tabId: tab.id, url: u.toString(), maxPages, activateTab });
3044
3085
  if (activateTab && tab.id != null) {
@@ -3066,13 +3107,24 @@ async function runLinkedInSearch(args, log, ctx) {
3066
3107
  const extracted = await cdpEvaluate(tab.id, LINKEDIN_EXTRACT_SOURCE);
3067
3108
  lastUrl = extracted?.url || lastUrl;
3068
3109
  const pageLeads = extracted?.links?.talent || [];
3110
+ let pageFiltered = 0;
3069
3111
  for (const l of pageLeads) {
3112
+ if ((locNeedle || coNeedle) && !matcher.match(l.ctx)) {
3113
+ pageFiltered++;
3114
+ continue;
3115
+ }
3070
3116
  if (l.url && !leads.has(l.url)) {
3071
- leads.set(l.url, { url: l.url, name: l.name ?? null, page, source: "linkedin" });
3117
+ leads.set(l.url, {
3118
+ url: l.url,
3119
+ name: l.name ?? null,
3120
+ page,
3121
+ source: "linkedin",
3122
+ ...l.ctx ? { snippet: l.ctx.slice(0, 200) } : {}
3123
+ });
3072
3124
  }
3073
3125
  }
3074
3126
  pagesScanned = page;
3075
- log("info", "hm.linkedin.search page extracted", { page, pageLeads: pageLeads.length, total: leads.size });
3127
+ log("info", "hm.linkedin.search page extracted", { page, pageLeads: pageLeads.length, pageFiltered, total: leads.size });
3076
3128
  if (page >= maxPages) break;
3077
3129
  const clicked = await cdpEvaluate(tab.id, LINKEDIN_NEXT_CLICK_SOURCE).catch(() => false);
3078
3130
  const urlBefore = (await chrome.tabs.get(tab.id).catch(() => null))?.url || "";
@@ -2339,6 +2339,31 @@ async function findDetailTabToReuse(url) {
2339
2339
  return idle?.id;
2340
2340
  }
2341
2341
 
2342
+ // src/background/linkedin-search-filter.ts
2343
+ var LINKEDIN_DEFAULT_LOAD_TIMEOUT_MS = 9e4;
2344
+ var MIN_TIMEOUT_MS = 5e3;
2345
+ var MAX_TIMEOUT_MS = 3e5;
2346
+ function clampLoadTimeoutMs(raw) {
2347
+ const n = Number(raw);
2348
+ if (!Number.isFinite(n) || n <= 0) return LINKEDIN_DEFAULT_LOAD_TIMEOUT_MS;
2349
+ return Math.min(Math.max(n, MIN_TIMEOUT_MS), MAX_TIMEOUT_MS);
2350
+ }
2351
+ function buildCardMatcher(filters) {
2352
+ const needle = (v) => typeof v === "string" && v.trim() ? v.trim().toLowerCase() : null;
2353
+ const locNeedle = filters.geoUrn ? null : needle(filters.location);
2354
+ const coNeedle = needle(filters.company);
2355
+ return {
2356
+ locNeedle,
2357
+ coNeedle,
2358
+ match(ctx) {
2359
+ const h = (ctx || "").toLowerCase();
2360
+ if (locNeedle && !h.includes(locNeedle)) return false;
2361
+ if (coNeedle && !h.includes(coNeedle)) return false;
2362
+ return true;
2363
+ }
2364
+ };
2365
+ }
2366
+
2342
2367
  // src/background/control.ts
2343
2368
  var RUNTIME_BRIDGE_FILE = "content/control-runtime.js";
2344
2369
  async function invokeRuntimeBridge(command, args) {
@@ -2940,12 +2965,25 @@ var LINKEDIN_EXTRACT_SOURCE = String.raw`(async () => {
2940
2965
  });
2941
2966
  } else if (pageClass === 'list') {
2942
2967
  const seen = new Set();
2968
+ // 卡片上下文:LinkedIn list 页 a[href*=/in/] 埋在结果卡内,向上找最小"像卡片"的祖先
2969
+ // (innerText 40-500 字符),抓 headline/地区/公司行——location/company 过滤的判定依据
2970
+ // (2026-10-18 寻访实测:LinkedIn 对自由文本 location/company 查询参数基本不生效,
2971
+ // 必须在 client 端按卡片文本过滤,否则过滤器名存实亡)。
2972
+ const cardCtx = (a) => {
2973
+ let el = a;
2974
+ for (let up = 0; up < 5 && el.parentElement; up++) {
2975
+ el = el.parentElement;
2976
+ const t = (el.innerText || '').replace(/\s+/g, ' ').trim();
2977
+ if (t.length >= 40) return t.slice(0, 400);
2978
+ }
2979
+ return null;
2980
+ };
2943
2981
  for (const a of Array.from(document.querySelectorAll('a[href]'))) {
2944
2982
  const href = a.href || '';
2945
2983
  let m = href.match(/linkedin\.com\/in\/([^/?#]+)/);
2946
2984
  if (m && !seen.has('t:' + m[1])) {
2947
2985
  seen.add('t:' + m[1]);
2948
- links.talent.push({ url: 'https://www.linkedin.com/in/' + m[1] + '/', name: (a.textContent || '').trim().split('\n')[0] || m[1] });
2986
+ links.talent.push({ url: 'https://www.linkedin.com/in/' + m[1] + '/', name: (a.textContent || '').trim().split('\n')[0] || m[1], ctx: cardCtx(a) });
2949
2987
  }
2950
2988
  m = href.match(/linkedin\.com\/company\/([^/?#]+)/);
2951
2989
  if (m && !seen.has('c:' + m[1])) {
@@ -3025,7 +3063,7 @@ async function runLinkedInSearch(args, log, ctx) {
3025
3063
  const maxPages = Math.min(Math.max(Number(args.maxPages) || 3, 1), 10);
3026
3064
  const keepTab = args.keepTab === true;
3027
3065
  const activateTab = args.activateTab !== false;
3028
- const loadTimeoutMs = Math.min(Math.max(Number(args.loadTimeoutMs) || 25e3, 5e3), 3e5);
3066
+ const loadTimeoutMs = clampLoadTimeoutMs(args.loadTimeoutMs);
3029
3067
  const delayBase = typeof args.delayMs === "number" ? Number(args.delayMs) : 2500;
3030
3068
  let effectiveKeywords = keywords;
3031
3069
  if (!filters.geoUrn && typeof filters.location === "string" && filters.location.trim()) {
@@ -3039,6 +3077,9 @@ async function runLinkedInSearch(args, log, ctx) {
3039
3077
  if (typeof filters.geoUrn === "string" && filters.geoUrn) u.searchParams.set("geoUrn", filters.geoUrn);
3040
3078
  if (typeof filters.company === "string" && filters.company) u.searchParams.set("company", filters.company);
3041
3079
  if (typeof filters.school === "string" && filters.school) u.searchParams.set("school", filters.school);
3080
+ const matcher = buildCardMatcher(filters);
3081
+ const locNeedle = matcher.locNeedle;
3082
+ const coNeedle = matcher.coNeedle;
3042
3083
  const tab = await chrome.tabs.create({ url: u.toString(), active: false });
3043
3084
  log("info", "hm.linkedin.search tab created", { tabId: tab.id, url: u.toString(), maxPages, activateTab });
3044
3085
  if (activateTab && tab.id != null) {
@@ -3066,13 +3107,24 @@ async function runLinkedInSearch(args, log, ctx) {
3066
3107
  const extracted = await cdpEvaluate(tab.id, LINKEDIN_EXTRACT_SOURCE);
3067
3108
  lastUrl = extracted?.url || lastUrl;
3068
3109
  const pageLeads = extracted?.links?.talent || [];
3110
+ let pageFiltered = 0;
3069
3111
  for (const l of pageLeads) {
3112
+ if ((locNeedle || coNeedle) && !matcher.match(l.ctx)) {
3113
+ pageFiltered++;
3114
+ continue;
3115
+ }
3070
3116
  if (l.url && !leads.has(l.url)) {
3071
- leads.set(l.url, { url: l.url, name: l.name ?? null, page, source: "linkedin" });
3117
+ leads.set(l.url, {
3118
+ url: l.url,
3119
+ name: l.name ?? null,
3120
+ page,
3121
+ source: "linkedin",
3122
+ ...l.ctx ? { snippet: l.ctx.slice(0, 200) } : {}
3123
+ });
3072
3124
  }
3073
3125
  }
3074
3126
  pagesScanned = page;
3075
- log("info", "hm.linkedin.search page extracted", { page, pageLeads: pageLeads.length, total: leads.size });
3127
+ log("info", "hm.linkedin.search page extracted", { page, pageLeads: pageLeads.length, pageFiltered, total: leads.size });
3076
3128
  if (page >= maxPages) break;
3077
3129
  const clicked = await cdpEvaluate(tab.id, LINKEDIN_NEXT_CLICK_SOURCE).catch(() => false);
3078
3130
  const urlBefore = (await chrome.tabs.get(tab.id).catch(() => null))?.url || "";
@@ -1 +1 @@
1
- {"buildTime":"2026-10-17T18:33:28.755Z","version":"0.1.25"}
1
+ {"buildTime":"2026-10-19T16:15:23.540Z","version":"0.1.26"}
@@ -2,8 +2,8 @@
2
2
  "manifest_version": 3,
3
3
  "minimum_chrome_version": "116",
4
4
  "name": "__MSG_extName__",
5
- "description": "[build 10-17 18:33:28] __MSG_extDescription__",
6
- "version": "0.1.25",
5
+ "description": "[build 10-19 16:15:23] __MSG_extDescription__",
6
+ "version": "0.1.26",
7
7
  "default_locale": "zh_CN",
8
8
  "key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAi/S88toqsiW6zleTj+V8jPUVTmrJiyDPvqTd+Cqrl/Ax9PCxaMsiXXUh2sNJh6sMj7xj0YBclCPMsI2wxIF8y4r6KnmWt49RuMg7EynvOT8fcRjxXBWGAz6hjGepWesOCMn9dgJ5IqgXP3j+Bfb+636IeW1nZxOedT25zJwJr81G4gIyc9kk+wCZPHUhQTsfNVxd8xfbxYQpcDuuVk1dDNEszeLUZGKYl9JcOQcaEQOWA4ED5avn67ZWQx5kzowB7XYtcTH0vz63cKNcK3O1iQ0hmFoohPKRWh9tNl545UQ2+mPaU6B/CNSZP2qwb+tgK7lVh5zG5qQkbaP8ZP8XaQIDAQAB",
9
9
  "permissions": [
@@ -27,7 +27,7 @@
27
27
  "<all_urls>"
28
28
  ],
29
29
  "background": {
30
- "service_worker": "background/service-worker.20261017183328.js",
30
+ "service_worker": "background/service-worker.20261019161523.js",
31
31
  "type": "module"
32
32
  },
33
33
  "content_scripts": [