hunter-mate 0.1.3 → 0.1.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 (101) hide show
  1. package/LICENSE +21 -0
  2. package/dist/assets/extension/background/service-worker.js +113 -24
  3. package/dist/assets/extension/content/index.js +239 -134
  4. package/dist/assets/extension/manifest.json +2 -4
  5. package/dist/assets/extension/sidepanel/index.js +76 -2
  6. package/dist/assets/server/app.module.js +4 -2
  7. package/dist/assets/server/app.module.js.map +1 -1
  8. package/dist/assets/server/build-info.json +1 -1
  9. package/dist/assets/server/db/sqlite.module.js +4 -0
  10. package/dist/assets/server/db/sqlite.module.js.map +1 -1
  11. package/dist/assets/server/devops/devops-sink-init.d.ts +14 -1
  12. package/dist/assets/server/devops/devops-sink-init.js +8 -3
  13. package/dist/assets/server/devops/devops-sink-init.js.map +1 -1
  14. package/dist/assets/server/doctor/doctor.controller.d.ts +13 -0
  15. package/dist/assets/server/doctor/doctor.controller.js +62 -0
  16. package/dist/assets/server/doctor/doctor.controller.js.map +1 -0
  17. package/dist/assets/server/doctor/doctor.module.d.ts +2 -0
  18. package/dist/assets/server/doctor/doctor.module.js +27 -0
  19. package/dist/assets/server/doctor/doctor.module.js.map +1 -0
  20. package/dist/assets/server/doctor/doctor.service.d.ts +15 -0
  21. package/dist/assets/server/doctor/doctor.service.js +209 -0
  22. package/dist/assets/server/doctor/doctor.service.js.map +1 -0
  23. package/dist/assets/server/doctor/doctor.types.d.ts +46 -0
  24. package/dist/assets/server/doctor/doctor.types.js +3 -0
  25. package/dist/assets/server/doctor/doctor.types.js.map +1 -0
  26. package/dist/assets/server/extraction/adapter.service.d.ts +18 -4
  27. package/dist/assets/server/extraction/adapter.service.js +129 -15
  28. package/dist/assets/server/extraction/adapter.service.js.map +1 -1
  29. package/dist/assets/server/extraction/anomaly-report.service.d.ts +12 -0
  30. package/dist/assets/server/extraction/anomaly-report.service.js +81 -0
  31. package/dist/assets/server/extraction/anomaly-report.service.js.map +1 -0
  32. package/dist/assets/server/extraction/entities/adapter-rule.entity.d.ts +13 -0
  33. package/dist/assets/server/extraction/entities/adapter-rule.entity.js +71 -0
  34. package/dist/assets/server/extraction/entities/adapter-rule.entity.js.map +1 -0
  35. package/dist/assets/server/extraction/entities/parse-log.entity.d.ts +4 -1
  36. package/dist/assets/server/extraction/entities/parse-log.entity.js +15 -0
  37. package/dist/assets/server/extraction/entities/parse-log.entity.js.map +1 -1
  38. package/dist/assets/server/extraction/extraction-entity.controller.d.ts +40 -2
  39. package/dist/assets/server/extraction/extraction-entity.controller.js +196 -13
  40. package/dist/assets/server/extraction/extraction-entity.controller.js.map +1 -1
  41. package/dist/assets/server/extraction/extraction-rule.service.js +16 -8
  42. package/dist/assets/server/extraction/extraction-rule.service.js.map +1 -1
  43. package/dist/assets/server/extraction/extraction.module.js +12 -2
  44. package/dist/assets/server/extraction/extraction.module.js.map +1 -1
  45. package/dist/assets/server/extraction/extraction.service.d.ts +17 -1
  46. package/dist/assets/server/extraction/extraction.service.js +125 -42
  47. package/dist/assets/server/extraction/extraction.service.js.map +1 -1
  48. package/dist/assets/server/extraction/linkedin-extractor.d.ts +1 -0
  49. package/dist/assets/server/extraction/linkedin-extractor.js +244 -0
  50. package/dist/assets/server/extraction/linkedin-extractor.js.map +1 -0
  51. package/dist/assets/server/extraction/rrweb-snapshot.utils.d.ts +15 -0
  52. package/dist/assets/server/extraction/rrweb-snapshot.utils.js +48 -0
  53. package/dist/assets/server/extraction/rrweb-snapshot.utils.js.map +1 -0
  54. package/dist/assets/server/extraction/rrweb-snapshot.utils.spec.d.ts +1 -0
  55. package/dist/assets/server/extraction/rrweb-snapshot.utils.spec.js +60 -0
  56. package/dist/assets/server/extraction/rrweb-snapshot.utils.spec.js.map +1 -0
  57. package/dist/assets/server/extraction/script-rule.d.ts +1 -0
  58. package/dist/assets/server/extraction/script-rule.js +4 -0
  59. package/dist/assets/server/extraction/script-rule.js.map +1 -1
  60. package/dist/assets/server/extraction/snapshot-analysis.service.d.ts +23 -0
  61. package/dist/assets/server/extraction/snapshot-analysis.service.js +147 -0
  62. package/dist/assets/server/extraction/snapshot-analysis.service.js.map +1 -0
  63. package/dist/assets/server/recording/entities/recording-snapshot.entity.d.ts +16 -0
  64. package/dist/assets/server/recording/entities/recording-snapshot.entity.js +91 -0
  65. package/dist/assets/server/recording/entities/recording-snapshot.entity.js.map +1 -0
  66. package/dist/assets/server/recording/recording-event-bus.service.d.ts +4 -0
  67. package/dist/assets/server/recording/recording-event-bus.service.js +25 -0
  68. package/dist/assets/server/recording/recording-event-bus.service.js.map +1 -0
  69. package/dist/assets/server/recording/recording-snapshot.service.d.ts +13 -0
  70. package/dist/assets/server/recording/recording-snapshot.service.js +81 -0
  71. package/dist/assets/server/recording/recording-snapshot.service.js.map +1 -0
  72. package/dist/assets/server/recording/recording-snapshot.service.spec.d.ts +1 -0
  73. package/dist/assets/server/recording/recording-snapshot.service.spec.js +65 -0
  74. package/dist/assets/server/recording/recording-snapshot.service.spec.js.map +1 -0
  75. package/dist/assets/server/recording/recording.controller.d.ts +3 -1
  76. package/dist/assets/server/recording/recording.controller.js +7 -3
  77. package/dist/assets/server/recording/recording.controller.js.map +1 -1
  78. package/dist/assets/server/recording/recording.module.js +11 -4
  79. package/dist/assets/server/recording/recording.module.js.map +1 -1
  80. package/dist/assets/server/recording/recording.service.d.ts +5 -21
  81. package/dist/assets/server/recording/recording.service.js +22 -9
  82. package/dist/assets/server/recording/recording.service.js.map +1 -1
  83. package/dist/assets/server/site/entities/site-registry.entity.d.ts +2 -0
  84. package/dist/assets/server/site/entities/site-registry.entity.js +10 -0
  85. package/dist/assets/server/site/entities/site-registry.entity.js.map +1 -1
  86. package/dist/assets/server/site/site.controller.d.ts +2 -0
  87. package/dist/assets/server/site/site.controller.js +1 -1
  88. package/dist/assets/server/site/site.controller.js.map +1 -1
  89. package/dist/assets/server/site/site.service.d.ts +2 -1
  90. package/dist/assets/server/site/site.service.js +10 -3
  91. package/dist/assets/server/site/site.service.js.map +1 -1
  92. package/dist/auto-doctor.d.ts +8 -0
  93. package/dist/auto-doctor.js +34 -0
  94. package/dist/commands/doctor.d.ts +38 -0
  95. package/dist/commands/doctor.js +162 -7
  96. package/dist/commands/extension.js +2 -2
  97. package/dist/commands/extras.d.ts +1 -1
  98. package/dist/commands/extras.js +4 -3
  99. package/dist/commands/install.js +11 -8
  100. package/dist/index.js +7 -0
  101. package/package.json +3 -3
@@ -10497,10 +10497,10 @@
10497
10497
  mutationBuffers.push(mutationBuffer);
10498
10498
  mutationBuffer.init(options);
10499
10499
  const [ObserverCtor, iframeCleanup] = mutationObserverCtor();
10500
- const observer = new ObserverCtor(
10500
+ const observer2 = new ObserverCtor(
10501
10501
  callbackWrapper(mutationBuffer.processMutations.bind(mutationBuffer))
10502
10502
  );
10503
- observer.observe(rootEl, {
10503
+ observer2.observe(rootEl, {
10504
10504
  attributes: true,
10505
10505
  attributeOldValue: true,
10506
10506
  characterData: true,
@@ -10508,7 +10508,7 @@
10508
10508
  childList: true,
10509
10509
  subtree: true
10510
10510
  });
10511
- return [observer, iframeCleanup];
10511
+ return [observer2, iframeCleanup];
10512
10512
  }
10513
10513
  function initMoveObserver({
10514
10514
  mousemoveCb,
@@ -11811,7 +11811,7 @@
11811
11811
  if (!isNativeShadowDom(shadowRoot2)) return;
11812
11812
  if (this.shadowDoms.has(shadowRoot2)) return;
11813
11813
  this.shadowDoms.add(shadowRoot2);
11814
- const [observer] = initMutationObserver(
11814
+ const [observer2] = initMutationObserver(
11815
11815
  {
11816
11816
  ...this.bypassOptions,
11817
11817
  doc,
@@ -11821,7 +11821,7 @@
11821
11821
  },
11822
11822
  shadowRoot2
11823
11823
  );
11824
- this.restoreHandlers.push(() => observer.disconnect());
11824
+ this.restoreHandlers.push(() => observer2.disconnect());
11825
11825
  this.restoreHandlers.push(
11826
11826
  initScrollObserver({
11827
11827
  ...this.bypassOptions,
@@ -12945,13 +12945,13 @@
12945
12945
  console.warn(error);
12946
12946
  }
12947
12947
  });
12948
- const init = () => {
12948
+ const init2 = () => {
12949
12949
  takeFullSnapshot$1();
12950
12950
  handlers.push(observe(document));
12951
12951
  recording = true;
12952
12952
  };
12953
12953
  if (["interactive", "complete"].includes(document.readyState)) {
12954
- init();
12954
+ init2();
12955
12955
  } else {
12956
12956
  handlers.push(
12957
12957
  on("DOMContentLoaded", () => {
@@ -12959,7 +12959,7 @@
12959
12959
  type: EventType.DomContentLoaded,
12960
12960
  data: {}
12961
12961
  });
12962
- if (recordAfter === "DOMContentLoaded") init();
12962
+ if (recordAfter === "DOMContentLoaded") init2();
12963
12963
  })
12964
12964
  );
12965
12965
  handlers.push(
@@ -12970,7 +12970,7 @@
12970
12970
  type: EventType.Load,
12971
12971
  data: {}
12972
12972
  });
12973
- if (recordAfter === "load") init();
12973
+ if (recordAfter === "load") init2();
12974
12974
  },
12975
12975
  window
12976
12976
  )
@@ -13046,20 +13046,20 @@
13046
13046
  }
13047
13047
  function patchNetwork() {
13048
13048
  const originalFetch = window.fetch;
13049
- window.fetch = async function patchedFetch(input2, init) {
13050
- const response = await originalFetch.apply(this, [input2, init]);
13049
+ window.fetch = async function patchedFetch(input2, init2) {
13050
+ const response = await originalFetch.apply(this, [input2, init2]);
13051
13051
  try {
13052
13052
  const url = typeof input2 === "string" ? input2 : input2.url;
13053
13053
  const requestHeaders = {};
13054
- if (init?.headers) {
13055
- new Headers(init.headers).forEach((value, key) => {
13054
+ if (init2?.headers) {
13055
+ new Headers(init2.headers).forEach((value, key) => {
13056
13056
  requestHeaders[key] = value;
13057
13057
  });
13058
13058
  }
13059
13059
  const event = (0, import_shared.createNetworkEvent)(
13060
13060
  sessionId,
13061
13061
  {
13062
- method: init?.method || "GET",
13062
+ method: init2?.method || "GET",
13063
13063
  url: (0, import_shared.redactUrlQuery)(url),
13064
13064
  requestHeaders: (0, import_shared.redactHeaders)(requestHeaders),
13065
13065
  responseHeaders: (0, import_shared.redactHeaders)(Object.fromEntries(response.headers.entries())),
@@ -13098,20 +13098,34 @@
13098
13098
 
13099
13099
  // src/content/recorder.ts
13100
13100
  var stopFn = null;
13101
+ var currentSessionId = null;
13102
+ var currentSourceUrl = "";
13103
+ var CHECKOUT_EVERY_NMS = 3e4;
13104
+ var CHECKOUT_EVERY_NTH = 200;
13101
13105
  function startRecording(sessionId4) {
13102
13106
  if (stopFn) return;
13103
13107
  setSessionId(sessionId4);
13104
13108
  setSessionId2(sessionId4);
13105
13109
  patchNetwork();
13106
13110
  patchConsole();
13107
- const sourceUrl = window.location.href;
13111
+ currentSessionId = sessionId4;
13112
+ currentSourceUrl = window.location.href;
13108
13113
  const stop = record({
13109
- emit(event) {
13110
- const hbrEvent = (0, import_shared3.createHbrEvent)("rrweb", sessionId4, event, { privacy: "private", sourceUrl });
13114
+ emit(event, isCheckout) {
13115
+ if (isCheckout != null && event && typeof event === "object") {
13116
+ event.isCheckout = isCheckout === true;
13117
+ }
13118
+ const hbrEvent = (0, import_shared3.createHbrEvent)("rrweb", sessionId4, event, {
13119
+ privacy: "private",
13120
+ sourceUrl: currentSourceUrl
13121
+ });
13111
13122
  sendToBackground({ type: "recording-event", payload: hbrEvent });
13112
13123
  },
13113
13124
  recordCanvas: false,
13114
- collectFonts: false
13125
+ collectFonts: false,
13126
+ checkoutEveryNms: CHECKOUT_EVERY_NMS,
13127
+ checkoutEveryNth: CHECKOUT_EVERY_NTH,
13128
+ maskAllInputs: true
13115
13129
  });
13116
13130
  if (!stop) return;
13117
13131
  stopFn = stop;
@@ -13121,9 +13135,22 @@
13121
13135
  if (stopFn) {
13122
13136
  stopFn();
13123
13137
  stopFn = null;
13138
+ currentSessionId = null;
13139
+ currentSourceUrl = "";
13124
13140
  console.log("[HunterMate] recording stopped");
13125
13141
  }
13126
13142
  }
13143
+ function setRecordingSourceUrl(url) {
13144
+ currentSourceUrl = url;
13145
+ }
13146
+ function takeCheckpoint(isCheckout = true) {
13147
+ if (!stopFn) return;
13148
+ try {
13149
+ record.takeFullSnapshot(isCheckout);
13150
+ } catch (err) {
13151
+ console.warn("[HunterMate] takeFullSnapshot failed", err);
13152
+ }
13153
+ }
13127
13154
 
13128
13155
  // src/shared/etld.ts
13129
13156
  var MULTI_PART_SUFFIXES = /* @__PURE__ */ new Set([
@@ -13228,101 +13255,6 @@
13228
13255
  return out;
13229
13256
  }
13230
13257
 
13231
- // src/content/floating-overlay.ts
13232
- var OVERLAY_ID = "hunter-mate-overlay";
13233
- function removeOverlay() {
13234
- const existing = document.getElementById(OVERLAY_ID);
13235
- if (existing) existing.remove();
13236
- }
13237
- async function showOverlay(pageInfo, parseResult) {
13238
- removeOverlay();
13239
- if (pageInfo.category === "unknown" || !parseResult) return;
13240
- const container2 = document.createElement("div");
13241
- container2.id = OVERLAY_ID;
13242
- container2.style.cssText = `
13243
- position: fixed;
13244
- top: 80px;
13245
- right: 20px;
13246
- width: 260px;
13247
- max-height: 360px;
13248
- overflow: auto;
13249
- background: #fff;
13250
- border: 1px solid #dadce0;
13251
- border-radius: 8px;
13252
- box-shadow: 0 4px 12px rgba(0,0,0,0.15);
13253
- z-index: 2147483647;
13254
- font-family: system-ui, -apple-system, sans-serif;
13255
- font-size: 13px;
13256
- padding: 12px;
13257
- color: #202124;
13258
- `;
13259
- const title = document.createElement("div");
13260
- title.style.cssText = "font-weight:bold;margin-bottom:8px;display:flex;align-items:center;gap:6px;";
13261
- const status = parseResult.parsedResumeId ? "parsed" : "parse-failed";
13262
- if (status === "parse-failed") {
13263
- title.innerHTML = "<span>\u274C</span><span>\u89E3\u6790\u5931\u8D25</span>";
13264
- container2.appendChild(title);
13265
- document.body.appendChild(container2);
13266
- return;
13267
- }
13268
- const isMerged = parseResult.matchType === "merged";
13269
- const resumeCount = parseResult.portraitResumeCount || 1;
13270
- if (isMerged && resumeCount > 1) {
13271
- title.innerHTML = `<span>\u2705</span><span>\u5DF2\u6536\u5F55 (\u5171 ${resumeCount} \u4EFD\u6765\u6E90)</span>`;
13272
- } else if (isMerged) {
13273
- title.innerHTML = "<span>\u{1F50D}</span><span>\u7591\u4F3C\u5DF2\u6536\u5F55 / \u540C\u4EBA</span>";
13274
- } else {
13275
- title.innerHTML = "<span>\u{1F195}</span><span>\u65B0\u4EBA\u624D\uFF0C\u5DF2\u5165\u5E93</span>";
13276
- }
13277
- container2.appendChild(title);
13278
- const meta = document.createElement("div");
13279
- meta.style.cssText = "color:#5f6368;margin-bottom:8px;";
13280
- meta.textContent = `\u7F6E\u4FE1\u5EA6 ${Math.round((parseResult.overallConfidence || 0) * 100)}% \xB7 \u753B\u50CF ${parseResult.portraitId?.slice(0, 8) || "N/A"}`;
13281
- container2.appendChild(meta);
13282
- if (pageInfo.category === "talent" && parseResult.portraitId) {
13283
- await renderRecommendedJobs(container2, parseResult.portraitId);
13284
- }
13285
- document.body.appendChild(container2);
13286
- setTimeout(() => {
13287
- container2.style.opacity = "0.95";
13288
- container2.style.transition = "opacity 0.3s";
13289
- }, 5e3);
13290
- }
13291
- async function renderRecommendedJobs(container2, portraitId) {
13292
- const header = document.createElement("div");
13293
- header.style.cssText = "font-weight:bold;margin:8px 0 4px;border-top:1px solid #eee;padding-top:8px;";
13294
- header.textContent = "\u63A8\u8350\u804C\u4F4D";
13295
- container2.appendChild(header);
13296
- try {
13297
- const res = await fetch(`${SERVER_HTTP_URL}/matching/portraits/${portraitId}/jobs`);
13298
- if (!res.ok) throw new Error(`status ${res.status}`);
13299
- const matches = (await res.json()).slice(0, 3);
13300
- if (!matches.length) {
13301
- const empty = document.createElement("div");
13302
- empty.style.cssText = "color:#888;";
13303
- empty.textContent = "\u6682\u65E0\u5339\u914D\u804C\u4F4D";
13304
- container2.appendChild(empty);
13305
- return;
13306
- }
13307
- for (const m of matches) {
13308
- const row = document.createElement("div");
13309
- row.style.cssText = "padding:6px 0;border-bottom:1px solid #f1f3f4;cursor:pointer;";
13310
- const score = m.scores?.overall ?? 0;
13311
- row.innerHTML = `<div style="font-weight:500;">\u804C\u4F4D #${m.jobCaseId}</div>
13312
- <div style="color:#5f6368;font-size:12px;">\u5339\u914D\u5EA6 ${score}%</div>`;
13313
- row.addEventListener("click", () => {
13314
- window.open(`http://localhost:3000/jobs/${m.jobCaseId}`, "_blank");
13315
- });
13316
- container2.appendChild(row);
13317
- }
13318
- } catch {
13319
- const err = document.createElement("div");
13320
- err.style.cssText = "color:#888;";
13321
- err.textContent = "\u63A8\u8350\u804C\u4F4D\u52A0\u8F7D\u5931\u8D25";
13322
- container2.appendChild(err);
13323
- }
13324
- }
13325
-
13326
13258
  // ../../node_modules/.pnpm/@chromepilot+logger-kit@0.1.3_@nestjs+common@10.4.22_class-transformer@0.5.1_class-validator@_v44hq6ikmqzoani6tawke4nway/node_modules/@chromepilot/logger-kit/dist/client/logger.js
13327
13259
  function stringifyArg(arg) {
13328
13260
  if (typeof arg === "string")
@@ -13488,18 +13420,105 @@
13488
13420
  });
13489
13421
  }
13490
13422
 
13423
+ // src/content/dom-watch.ts
13424
+ var BIG_DOM_THRESHOLD = 50;
13425
+ var SNAPSHOT_COOLDOWN_MS = 1e3;
13426
+ var lastSnapshotAt = 0;
13427
+ var observer = null;
13428
+ var onBigChangeCallback = null;
13429
+ var originalPushState = null;
13430
+ var originalReplaceState = null;
13431
+ function canTakeCheckpoint() {
13432
+ return Date.now() - lastSnapshotAt > SNAPSHOT_COOLDOWN_MS;
13433
+ }
13434
+ function markCheckpointTaken() {
13435
+ lastSnapshotAt = Date.now();
13436
+ }
13437
+ function patchHistory(onChange) {
13438
+ if (originalPushState) return;
13439
+ originalPushState = history.pushState;
13440
+ originalReplaceState = history.replaceState;
13441
+ history.pushState = function(...args) {
13442
+ originalPushState.apply(this, args);
13443
+ onChange();
13444
+ };
13445
+ history.replaceState = function(...args) {
13446
+ originalReplaceState.apply(this, args);
13447
+ onChange();
13448
+ };
13449
+ window.addEventListener("popstate", onChange);
13450
+ }
13451
+ function restoreHistory() {
13452
+ if (originalPushState) {
13453
+ history.pushState = originalPushState;
13454
+ originalPushState = null;
13455
+ }
13456
+ if (originalReplaceState) {
13457
+ history.replaceState = originalReplaceState;
13458
+ originalReplaceState = null;
13459
+ }
13460
+ }
13461
+ function initDomWatch(onBigChange) {
13462
+ onBigChangeCallback = onBigChange;
13463
+ patchHistory(() => {
13464
+ });
13465
+ if (typeof MutationObserver !== "undefined" && document.body) {
13466
+ let buffer = [];
13467
+ let flushTimer = null;
13468
+ const flush = () => {
13469
+ flushTimer = null;
13470
+ const mutations = buffer;
13471
+ buffer = [];
13472
+ let changedNodes = 0;
13473
+ for (const m of mutations) {
13474
+ changedNodes += m.addedNodes.length + m.removedNodes.length;
13475
+ }
13476
+ if (changedNodes > BIG_DOM_THRESHOLD && canTakeCheckpoint()) {
13477
+ onBigChangeCallback?.();
13478
+ }
13479
+ };
13480
+ observer = new MutationObserver((mutations) => {
13481
+ buffer.push(...mutations);
13482
+ if (!flushTimer) {
13483
+ flushTimer = setTimeout(flush, 200);
13484
+ }
13485
+ });
13486
+ observer.observe(document.body, { childList: true, subtree: true });
13487
+ }
13488
+ }
13489
+ function destroyDomWatch() {
13490
+ observer?.disconnect();
13491
+ observer = null;
13492
+ restoreHistory();
13493
+ onBigChangeCallback = null;
13494
+ }
13495
+
13491
13496
  // src/content/index.ts
13492
13497
  function generateSessionId() {
13493
13498
  return `rec-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
13494
13499
  }
13495
13500
  var sessionId3 = null;
13496
13501
  var autoParseStarted = false;
13502
+ var currentSitePolicy = null;
13503
+ var currentSourceUrl2 = window.location.href;
13497
13504
  var contentLogger = createMessageLogger({
13498
13505
  context: "content",
13499
13506
  sessionId: () => sessionId3 || `tab-${location.hostname}`
13500
13507
  });
13501
13508
  contentLogger.patchConsole();
13502
13509
  var ACTIONABLE_CLASSES = /* @__PURE__ */ new Set(["talent", "job", "company"]);
13510
+ async function querySitePolicy() {
13511
+ try {
13512
+ const policy = await chrome.runtime.sendMessage({
13513
+ type: "query-site-policy",
13514
+ payload: { url: window.location.href }
13515
+ });
13516
+ currentSitePolicy = policy || { record: false, parse: false };
13517
+ } catch {
13518
+ currentSitePolicy = { record: false, parse: false };
13519
+ }
13520
+ return currentSitePolicy || { record: false, parse: false };
13521
+ }
13503
13522
  function beginRecording() {
13504
13523
  if (sessionId3) return sessionId3;
13505
13524
  const cls = classifyPage();
@@ -13512,11 +13531,19 @@
13512
13531
  return null;
13513
13532
  }
13514
13533
  sessionId3 = generateSessionId();
13534
+ currentSourceUrl2 = window.location.href;
13515
13535
  startRecording(sessionId3);
13516
13536
  sendToBackground({
13517
13537
  type: "recording-started",
13518
13538
  payload: { sessionId: sessionId3, classification: cls }
13519
13539
  });
13540
+ initDomWatch(() => {
13541
+ if (sessionId3 && canTakeCheckpoint2()) {
13542
+ takeCheckpoint(false);
13543
+ markCheckpointTaken();
13544
+ contentLogger.info("content:checkpoint:dom-big-change", { sessionId: sessionId3, url: window.location.href });
13545
+ }
13546
+ });
13520
13547
  beginAutoParse(cls);
13521
13548
  return sessionId3;
13522
13549
  }
@@ -13524,11 +13551,16 @@
13524
13551
  if (!sessionId3) return;
13525
13552
  const ended = sessionId3;
13526
13553
  stopRecording();
13554
+ destroyDomWatch();
13527
13555
  sendToBackground({ type: "recording-stopped", payload: { sessionId: ended } });
13528
13556
  sessionId3 = null;
13529
13557
  autoParseStarted = false;
13558
+ currentSourceUrl2 = "";
13530
13559
  return ended;
13531
13560
  }
13561
+ function canTakeCheckpoint2() {
13562
+ return true;
13563
+ }
13532
13564
  async function beginAutoParse(cls) {
13533
13565
  if (autoParseStarted) return;
13534
13566
  if (!ACTIONABLE_CLASSES.has(cls.pageClass)) {
@@ -13538,14 +13570,22 @@
13538
13570
  autoParseStarted = true;
13539
13571
  updatePageStatus(cls, "parsing");
13540
13572
  await waitForStable(2500, 500);
13541
- const currentSessionId = sessionId3;
13542
- if (!currentSessionId) {
13573
+ const currentSessionId2 = sessionId3;
13574
+ if (!currentSessionId2) {
13543
13575
  updatePageStatus(cls, "parse-failed");
13544
13576
  return;
13545
13577
  }
13546
- contentLogger.info("content:auto-parse:start", { sessionId: currentSessionId, pageClass: cls.pageClass });
13578
+ if (cls.pageClass === "talent" || cls.pageClass === "job" || cls.pageClass === "company") {
13579
+ await whenVisible();
13580
+ await scrollToLoadFullPage(5e3);
13581
+ await scrollSections(["Experience", "Education", "Skills", "Projects"]);
13582
+ takeCheckpoint(false);
13583
+ markCheckpointTaken();
13584
+ await waitForStable(1200, 300);
13585
+ }
13586
+ contentLogger.info("content:auto-parse:start", { sessionId: currentSessionId2, pageClass: cls.pageClass });
13547
13587
  try {
13548
- const parseRes = await fetch(`${SERVER_HTTP_URL}/parse/${currentSessionId}`, {
13588
+ const parseRes = await fetch(`${SERVER_HTTP_URL}/parse/${currentSessionId2}`, {
13549
13589
  method: "POST"
13550
13590
  });
13551
13591
  if (!parseRes.ok) {
@@ -13555,26 +13595,15 @@
13555
13595
  const result2 = await parseRes.json();
13556
13596
  const ok = result2.status === "ok" && result2.entity;
13557
13597
  contentLogger.info("content:auto-parse:done", {
13558
- sessionId: currentSessionId,
13598
+ sessionId: currentSessionId2,
13559
13599
  status: result2.status,
13560
13600
  ok,
13561
13601
  entityId: result2.entity?.id
13562
13602
  });
13563
13603
  updatePageStatus(cls, ok ? "parsed" : "parse-failed", result2);
13564
- if (ok) {
13565
- showOverlay(
13566
- { category: cls.pageClass, system: cls.domain, url: window.location.href },
13567
- {
13568
- snapshotId: currentSessionId,
13569
- parsedResumeId: String(result2.entity.id),
13570
- overallConfidence: result2.extracted ? 0.8 : 0.2,
13571
- matchType: result2.entity.isNew === false ? "merged" : "new"
13572
- }
13573
- );
13574
- }
13575
13604
  } catch (err) {
13576
13605
  contentLogger.error("content:auto-parse:failed", {
13577
- sessionId: currentSessionId,
13606
+ sessionId: currentSessionId2,
13578
13607
  error: err.message || String(err)
13579
13608
  });
13580
13609
  updatePageStatus(cls, "parse-failed");
@@ -13599,14 +13628,90 @@
13599
13628
  function waitForStable(baseMs, jitterMs) {
13600
13629
  return new Promise((resolve) => setTimeout(resolve, baseMs + Math.random() * jitterMs));
13601
13630
  }
13602
- beginRecording();
13631
+ function whenVisible() {
13632
+ if (!document.hidden) return Promise.resolve();
13633
+ return new Promise((resolve) => {
13634
+ const handler = () => {
13635
+ if (document.visibilityState === "visible") {
13636
+ document.removeEventListener("visibilitychange", handler);
13637
+ resolve();
13638
+ }
13639
+ };
13640
+ document.addEventListener("visibilitychange", handler);
13641
+ });
13642
+ }
13643
+ function scrollToLoadFullPage(durationMs = 5e3) {
13644
+ return new Promise((resolve) => {
13645
+ const start = performance.now();
13646
+ const startY = window.scrollY;
13647
+ const docHeight = () => document.body.scrollHeight || document.documentElement.scrollHeight;
13648
+ let lastHeight = docHeight();
13649
+ let lastChange = start;
13650
+ const maxMs = Math.max(durationMs * 3, 15e3);
13651
+ const stableThresholdMs = 1500;
13652
+ const interval = setInterval(() => {
13653
+ const now = performance.now();
13654
+ const elapsed = now - start;
13655
+ const target = Math.max(0, docHeight() - window.innerHeight);
13656
+ let nextY;
13657
+ if (elapsed < durationMs) {
13658
+ nextY = Math.min(target, target * elapsed / durationMs);
13659
+ } else {
13660
+ nextY = target;
13661
+ }
13662
+ window.scrollTo({ top: nextY, behavior: "auto" });
13663
+ const h = docHeight();
13664
+ if (h > lastHeight) {
13665
+ lastHeight = h;
13666
+ lastChange = now;
13667
+ }
13668
+ const stable = now - lastChange >= stableThresholdMs;
13669
+ if (stable && elapsed > durationMs || elapsed > maxMs) {
13670
+ clearInterval(interval);
13671
+ window.scrollTo({ top: startY, behavior: "auto" });
13672
+ resolve();
13673
+ }
13674
+ }, 150);
13675
+ });
13676
+ }
13677
+ async function scrollSections(keywords) {
13678
+ const headings = Array.from(document.querySelectorAll("h2")).filter(
13679
+ (h) => keywords.some((kw) => h.textContent?.includes(kw))
13680
+ );
13681
+ for (const h of headings) {
13682
+ h.scrollIntoView({ block: "center", behavior: "auto" });
13683
+ await waitForStable(600, 100);
13684
+ }
13685
+ }
13686
+ async function init() {
13687
+ const policy = await querySitePolicy();
13688
+ if (!policy.record) {
13689
+ contentLogger.info("content:site-not-target", { url: window.location.href });
13690
+ return;
13691
+ }
13692
+ beginRecording();
13693
+ }
13694
+ init();
13603
13695
  var lastUrl = window.location.href;
13604
13696
  setInterval(() => {
13605
13697
  if (window.location.href !== lastUrl) {
13606
13698
  lastUrl = window.location.href;
13607
- sessionId3 = null;
13608
- autoParseStarted = false;
13609
- beginRecording();
13699
+ currentSourceUrl2 = lastUrl;
13700
+ setRecordingSourceUrl(lastUrl);
13701
+ if (sessionId3) {
13702
+ takeCheckpoint(true);
13703
+ markCheckpointTaken();
13704
+ contentLogger.info("content:checkpoint:route-change", { sessionId: sessionId3, url: lastUrl });
13705
+ autoParseStarted = false;
13706
+ const cls = classifyPage();
13707
+ sendToBackground({
13708
+ type: "recording-started",
13709
+ payload: { sessionId: sessionId3, classification: cls }
13710
+ });
13711
+ beginAutoParse(cls);
13712
+ } else {
13713
+ init();
13714
+ }
13610
13715
  }
13611
13716
  }, 1e3);
13612
13717
  window.addEventListener("beforeunload", () => {
@@ -29,9 +29,7 @@
29
29
  "content_scripts": [
30
30
  {
31
31
  "matches": [
32
- "https://*.linkedin.com/*",
33
- "https://*.zhipin.com/*",
34
- "https://*.liepin.com/*"
32
+ "https://*/*"
35
33
  ],
36
34
  "js": [
37
35
  "content/index.js"
@@ -45,5 +43,5 @@
45
43
  "action": {
46
44
  "default_title": "__MSG_extActionTitle__"
47
45
  },
48
- "buildTime": "2026-08-30T16:26:46.364Z"
46
+ "buildTime": "2026-08-31T15:36:31.949Z"
49
47
  }