ilml-plugin-linkedin 1.20.1 → 1.20.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,8 +1,8 @@
1
- var yt=Object.defineProperty;var Q=(e,t)=>()=>(e&&(t=e(e=0)),t);var bt=(e,t)=>{for(var n in t)yt(e,n,{get:t[n],enumerable:!0})};async function g(e){let t=Math.floor(Math.random()*2001),n=e+t;return new Promise(o=>setTimeout(o,n))}async function Re(e){return await e.evaluate(()=>{let t=document.querySelectorAll("div.artdeco-modal--is-open");for(let o of t){let i=o.innerText||"",s=i.includes("Premium")||i.includes("premium")||i.includes("Upgrade")||i.includes("upgrade"),r=i.includes("Try for")||i.includes("free trial")||i.includes("/month")||i.includes("per month")||i.includes("Start my")||i.includes("Subscribe"),y=i.includes("Get hired faster")||i.includes("Get hired 2x");if(s||r||y){let m=o.querySelector('button[aria-label="Dismiss"]')||o.querySelector("button.artdeco-modal__dismiss")||o.querySelector("button[data-test-modal-close-btn]");if(m)return m.click(),{dismissed:!0,matched:i.substring(0,80)};let $=o.querySelectorAll("button");for(let c of $){let k=c.innerText?.trim().toLowerCase()||"",h=c.getAttribute("aria-label")?.toLowerCase()||"";if(k==="not now"||k==="no thanks"||k==="dismiss"||h.includes("dismiss"))return c.click(),{dismissed:!0,matched:i.substring(0,80)}}}}let n=document.querySelector(".premium-upsell-link")||document.querySelector("[data-test-premium-upsell]")||document.querySelector(".premium-hub-upsell");if(n){let o=n.closest('[class*="modal"]')?.querySelector('button[aria-label="Dismiss"]')||n.closest('[class*="banner"]')?.querySelector('button[aria-label="Close"]');if(o)return o.click(),{dismissed:!0,matched:"premium-banner"}}return!1})}var ue=Q(()=>{});import{getPluginConfig as St}from"@ilivemylife/graph-sdk";var kt,Gt,O,zt,Qt,Te=Q(async()=>{kt=await St();for(let[e,t]of Object.entries(kt))process.env[e]===void 0&&(process.env[e]=t);Gt=process.env.LINKEDIN_NAME,O=Object.freeze({DRAFT:"draft",CONFIRMED:"confirmed",REJECTED:"rejected",NEEDS_REVIEW:"needs_review",SENT:"sent"}),zt=Object.freeze(new Set([O.DRAFT,O.CONFIRMED,O.NEEDS_REVIEW])),Qt=Object.freeze(new Set([O.DRAFT,O.CONFIRMED,O.REJECTED,O.NEEDS_REVIEW]))});import"dotenv/config";import*as f from"fs";import*as b from"path";import{fileURLToPath as Ct}from"url";function qe(){f.existsSync(_)||f.mkdirSync(_,{recursive:!0})}function Be(e,t){let n=JSON.stringify(t,null,2),o=e+".tmp";f.writeFileSync(o,n);for(let i=0;i<3;i++)try{f.renameSync(o,e);return}catch(s){if(i<2&&(s.code==="EPERM"||s.code==="EBUSY")){let r=Date.now();for(;Date.now()-r<200;);continue}throw s}}function Ue(e,t){if(!f.existsSync(e))return null;try{let n=JSON.parse(f.readFileSync(e,"utf-8"));try{f.copyFileSync(e,e+".backup")}catch{}return n}catch(n){console.error(`[!] ${t} is corrupted: ${n.message}`),console.error(` File: ${b.resolve(e)}`);let o=e+".backup";if(f.existsSync(o))try{let i=JSON.parse(f.readFileSync(o,"utf-8"));console.error(` Restored from ${o}`);try{f.copyFileSync(o,e)}catch{}return i}catch{console.error(" Backup also corrupted!")}console.error(` Starting with empty data. Old file preserved as ${e}.corrupted`);try{f.copyFileSync(e,e+".corrupted")}catch{}return null}}function Et(){return new Date().toISOString().split("T")[0]}function q(e){if(!e)return null;let t=e.match(/\/in\/([^/?#]+)/);return t?t[1].toLowerCase():null}function Fe(e){return typeof e=="string"?e.replace(/\s+/g," ").trim().toLowerCase():""}function He(e,t,n=null){if(!t||e[t])return t;let o=q(t);if(!o)return t;let i=null;for(let[s,r]of Object.entries(e)){if(q(r.profileUrl||s)===o)return s;!i&&Array.isArray(r.aliases)&&r.aliases.includes(o)&&(i=s)}if(i){let s=Fe(n),r=Fe(e[i].name);if(s&&r&&s===r)return i}return t}function _t(){f.existsSync(fe)||f.mkdirSync(fe,{recursive:!0})}function Ve(e){let t=q(e);return t?b.join(fe,`${t}.json`):null}function $t(e){let t=Ve(e);return t?Ue(t,`profile-history/${q(e)}.json`)||[]:[]}function Dt(e,t){let n=Ve(e);if(!n)return;_t();let o=$t(e);o.unshift(t);try{Be(n,o)}catch(i){console.error(`[!] Failed to append profile-history for ${q(e)}: ${i.message}`)}}function Pe(e){return!!(e==null||Array.isArray(e)&&e.length===0||typeof e=="string"&&!e.trim())}function je(e){return Array.isArray(e)?[...e].map(t=>String(t).trim()).sort().join("||"):typeof e=="string"?e.replace(/\s+/g," ").trim():e}function Lt(e,t){if(!e)return[];let n=[];for(let o of At){let i=e[o],s=t[o];Pe(i)||Pe(s)||je(i)!==je(s)&&n.push(o)}return n}function Nt(e){let t=new Set(["seenCount","sources","firstSeen","lastSeen","lastUpdated","encounters","jobIds","lastFullVisitAt","lastSnapshotAt","messageSent","connectionStatus","connectionDate","connectionMethod"]),n={};for(let[o,i]of Object.entries(e))t.has(o)||(n[o]=i);return n}function me(){return W||(qe(),W=Ue(de,"people.json")||{},W)}function We(e){qe(),W=e;try{Be(de,e)}catch(t){console.error(`[!] Failed to save people.json: ${t.message}`),console.error(` Path: ${b.resolve(de)}`)}}function pe(e,t={}){if(!e)return null;let n=me(),o=Et(),i=new Date().toISOString(),s=He(n,e,t.name),r=n[s];if(r){let y=Lt(r,t);y.length>0&&(Dt(s,{capturedAt:i,previousLastUpdated:r.lastUpdated||null,changedFields:y,snapshot:Nt(r)}),r.lastSnapshotAt=i),r.seenCount=(r.seenCount||1)+1,r.lastSeen=o,r.lastUpdated=i,t.source&&!r.sources?.includes(t.source)&&(r.sources=r.sources||[],r.sources.push(t.source));for(let m of It)if(t[m]!=null){if(Array.isArray(t[m])&&t[m].length===0&&Array.isArray(r[m])&&r[m].length>0)continue;r[m]=t[m]}Array.isArray(t.availableSections)&&(r.lastFullVisitAt=i)}else n[s]={name:t.name||null,title:t.title||null,location:t.location||null,company:t.company||null,about:t.about||null,connectionDegree:t.connectionDegree||null,connectionsCount:t.connectionsCount||null,mutualConnections:t.mutualConnections||null,photoUrl:t.photoUrl||null,hasVerifiedBadge:t.hasVerifiedBadge||!1,currentCompany:t.currentCompany||null,currentSchool:t.currentSchool||null,connectionsLevel:t.connectionsLevel||null,mutualConnectionsCount:t.mutualConnectionsCount||null,followers:t.followers||null,openToWork:t.openToWork||!1,hiring:t.hiring||!1,topSkills:t.topSkills||null,availableSections:t.availableSections||null,experience:t.experience||null,featuredLinks:t.featuredLinks||null,_lastEnrichedAt:t._lastEnrichedAt||null,featured:t.featured||null,education:t.education||null,skillsFull:t.skillsFull||null,recommendations:t.recommendations||null,certifications:t.certifications||null,firstSeen:o,lastSeen:o,seenCount:1,sources:t.source?[t.source]:[],connectionStatus:null,requestedConnectionAt:null,connectionDate:null,connectionMethod:null,messageSent:null,jobIds:[],lastUpdated:i,lastSnapshotAt:null,lastFullVisitAt:Array.isArray(t.availableSections)?i:null};return We(n),n[s]}function he(e,t,n={}){if(n.dryRun||!e||!t)return;let o=q(t);if(!o)return;let i=me(),s=He(i,e),r=i[s];if(!r)return;let y=q(r.profileUrl||s);o!==y&&(Array.isArray(r.aliases)||(r.aliases=[]),r.aliases.includes(o)||(r.aliases.push(o),r.lastUpdated=new Date().toISOString(),We(i)))}var vt,xt,_,de,nn,on,rn,sn,ln,fe,Oe,an,cn,W,It,At,we=Q(async()=>{await Te();vt=Ct(import.meta.url),xt=b.dirname(b.dirname(vt)),_=process.env.DATA_DIR?b.resolve(process.env.DATA_DIR,"collected-profiles"):b.join(xt,"collected-profiles"),de=b.join(_,"people.json"),nn=b.join(_,"jobs.json"),on=b.join(_,"quota.json"),rn=b.join(_,"conversations.json"),sn=b.join(_,"sync-state.json"),ln=b.join(_,"application-questions.json"),fe=b.join(_,"profile-history"),Oe=(e,t)=>{let n=parseInt(e,10);return Number.isInteger(n)&&n>=0?n:t},an=Oe(process.env.CONNECT_MAX_DAILY,20),cn=Oe(process.env.CONNECT_MAX_WEEKLY,100),W=null;It=["name","title","location","company","connectionDegree","connectionsCount","mutualConnections","photoUrl","hasVerifiedBadge","about","currentCompany","currentSchool","connectionsLevel","mutualConnectionsCount","followers","openToWork","hiring","topSkills","availableSections","experience","featuredLinks","_lastEnrichedAt","featured","education","skillsFull","recommendations","certifications"],At=["name","title","location","currentCompany","currentSchool","connectionDegree","hasVerifiedBadge","openToWork","hiring","about","topSkills"]});var Ke={};bt(Ke,{extractCertificationsFromDetails:()=>Pt,extractEducationFromDetails:()=>Tt,extractExperienceFromDetails:()=>Mt,extractFeaturedFromDetails:()=>Rt,extractRecommendationsFromDetails:()=>jt,extractSkillsFromDetails:()=>Ft});async function Mt(e,t){let n=t.replace(/\/+$/,"")+"/details/experience/";try{await e.goto(n,{waitUntil:"domcontentloaded",timeout:6e4}),await g(3e3);let o=e.url();if(/\/(login|authwall|checkpoint)/i.test(o))throw new Error(`Session expired \u2014 bounced to ${o} when fetching /details/experience/`);for(let s=0;s<5;s++)await e.evaluate(()=>window.scrollTo(0,document.body.scrollHeight)),await g(900);await g(1e3);let i=0;for(;i<5&&await e.evaluate(()=>{for(let r of document.querySelectorAll("button"))if(/^Load more$/i.test((r.textContent||"").trim()))return r.click(),!0;return!1});)i++,await g(1500),await e.evaluate(()=>window.scrollTo(0,document.body.scrollHeight)),await g(700);return await e.evaluate(()=>{let s=document.querySelector("main");if(!s)return[];function r(c){let k=[];for(let h of c.querySelectorAll("div")){let w=Array.from(h.children).filter(E=>E.tagName==="DIV");if(w.length<3)continue;let S=w.filter(E=>(E.textContent||"").trim().length>50);S.length<3||(h.textContent||"").length<500||k.push({el:h,count:S.length,totalText:(h.textContent||"").length})}return k.sort((h,w)=>w.count-h.count||h.totalText-w.totalText),k[0]?.el}let y=r(s);if(!y)return[];let m=Array.from(y.children).filter(c=>c.tagName==="DIV").filter(c=>(c.textContent||"").trim().length>50);function $(c){let k=c[0]||null,h=null,w=null;if(c[1]){let d=c[1].split("\xB7").map(C=>C.trim());h=d[0]||null,d[1]&&(w=d[1])}let S=c.findIndex(d=>/(\d{4}|present)/i.test(d)&&/(-|–|—)/i.test(d)&&d.length<80),E=null,R=null;if(S>=0){let d=c[S].split("\xB7").map(C=>C.trim());E=d[0]||null,d[1]&&(R=d[1])}let T=null,F=-1;if(S>=0&&c[S+1]){let d=c[S+1],C=/^[>*\-•]|^&gt;/.test(d),U=/https?:\/\//.test(d),H=/\d{4}/.test(d),v=/,/.test(d)||/\bArea\b|\bRegion\b|\bProvince\b|\bCountry\b|, [A-Z]{2}\b/.test(d);!C&&!U&&!H&&d.length<100&&v&&(T=d,F=S+1)}let le=null,p=-1,P=c[c.length-1];if(P&&/and\s+\+\d+\s+skills?$/i.test(P)){p=c.length-1;let d=P.match(/^(.+?)\s+and\s+\+(\d+)\s+skills?$/i);d&&(le={listed:d[1].split(",").map(C=>C.trim()),additional:parseInt(d[2],10)})}let j=new Set([0,1,S,F,p].filter(d=>d>=0)),D=c.filter((d,C)=>!j.has(C)),x=D.length>0?D.join(`
1
+ var yt=Object.defineProperty;var Q=(e,t)=>()=>(e&&(t=e(e=0)),t);var bt=(e,t)=>{for(var n in t)yt(e,n,{get:t[n],enumerable:!0})};async function g(e){let t=Math.floor(Math.random()*2001),n=e+t;return new Promise(o=>setTimeout(o,n))}async function Re(e){return await e.evaluate(()=>{let t=document.querySelectorAll("div.artdeco-modal--is-open");for(let o of t){let i=o.innerText||"",s=i.includes("Premium")||i.includes("premium")||i.includes("Upgrade")||i.includes("upgrade"),r=i.includes("Try for")||i.includes("free trial")||i.includes("/month")||i.includes("per month")||i.includes("Start my")||i.includes("Subscribe"),y=i.includes("Get hired faster")||i.includes("Get hired 2x");if(s||r||y){let m=o.querySelector('button[aria-label="Dismiss"]')||o.querySelector("button.artdeco-modal__dismiss")||o.querySelector("button[data-test-modal-close-btn]");if(m)return m.click(),{dismissed:!0,matched:i.substring(0,80)};let $=o.querySelectorAll("button");for(let c of $){let k=c.innerText?.trim().toLowerCase()||"",h=c.getAttribute("aria-label")?.toLowerCase()||"";if(k==="not now"||k==="no thanks"||k==="dismiss"||h.includes("dismiss"))return c.click(),{dismissed:!0,matched:i.substring(0,80)}}}}let n=document.querySelector(".premium-upsell-link")||document.querySelector("[data-test-premium-upsell]")||document.querySelector(".premium-hub-upsell");if(n){let o=n.closest('[class*="modal"]')?.querySelector('button[aria-label="Dismiss"]')||n.closest('[class*="banner"]')?.querySelector('button[aria-label="Close"]');if(o)return o.click(),{dismissed:!0,matched:"premium-banner"}}return!1})}var ue=Q(()=>{});import{getPluginConfig as St}from"@ilivemylife/graph-sdk";var kt,Gt,O,zt,Qt,Te=Q(async()=>{kt=await St();for(let[e,t]of Object.entries(kt))process.env[e]===void 0&&(process.env[e]=t);Gt=process.env.LINKEDIN_NAME,O=Object.freeze({DRAFT:"draft",CONFIRMED:"confirmed",REJECTED:"rejected",NEEDS_REVIEW:"needs_review",SENT:"sent"}),zt=Object.freeze(new Set([O.DRAFT,O.CONFIRMED,O.NEEDS_REVIEW])),Qt=Object.freeze(new Set([O.DRAFT,O.CONFIRMED,O.REJECTED,O.NEEDS_REVIEW]))});import"dotenv/config";import*as f from"fs";import*as b from"path";import{fileURLToPath as Ct}from"url";function qe(){f.existsSync(_)||f.mkdirSync(_,{recursive:!0})}function Be(e,t){let n=JSON.stringify(t,null,2),o=e+".tmp";f.writeFileSync(o,n);for(let i=0;i<3;i++)try{f.renameSync(o,e);return}catch(s){if(i<2&&(s.code==="EPERM"||s.code==="EBUSY")){let r=Date.now();for(;Date.now()-r<200;);continue}throw s}}function Ue(e,t){if(!f.existsSync(e))return null;try{let n=JSON.parse(f.readFileSync(e,"utf-8"));try{f.copyFileSync(e,e+".backup")}catch{}return n}catch(n){console.error(`[!] ${t} is corrupted: ${n.message}`),console.error(` File: ${b.resolve(e)}`);let o=e+".backup";if(f.existsSync(o))try{let i=JSON.parse(f.readFileSync(o,"utf-8"));console.error(` Restored from ${o}`);try{f.copyFileSync(o,e)}catch{}return i}catch{console.error(" Backup also corrupted!")}console.error(` Starting with empty data. Old file preserved as ${e}.corrupted`);try{f.copyFileSync(e,e+".corrupted")}catch{}return null}}function Et(){return new Date().toISOString().split("T")[0]}function q(e){if(!e)return null;let t=e.match(/\/in\/([^/?#]+)/);return t?t[1].toLowerCase():null}function Fe(e){return typeof e=="string"?e.replace(/\s+/g," ").trim().toLowerCase():""}function He(e,t,n=null){if(!t||e[t])return t;let o=q(t);if(!o)return t;let i=null;for(let[s,r]of Object.entries(e)){if(q(r.profileUrl||s)===o)return s;!i&&Array.isArray(r.aliases)&&r.aliases.includes(o)&&(i=s)}if(i){let s=Fe(n),r=Fe(e[i].name);if(s&&r&&s===r)return i}return t}function _t(){f.existsSync(fe)||f.mkdirSync(fe,{recursive:!0})}function Ve(e){let t=q(e);return t?b.join(fe,`${t}.json`):null}function $t(e){let t=Ve(e);return t?Ue(t,`profile-history/${q(e)}.json`)||[]:[]}function Dt(e,t){let n=Ve(e);if(!n)return;_t();let o=$t(e);o.unshift(t);try{Be(n,o)}catch(i){console.error(`[!] Failed to append profile-history for ${q(e)}: ${i.message}`)}}function Pe(e){return!!(e==null||Array.isArray(e)&&e.length===0||typeof e=="string"&&!e.trim())}function je(e){return Array.isArray(e)?[...e].map(t=>String(t).trim()).sort().join("||"):typeof e=="string"?e.replace(/\s+/g," ").trim():e}function Lt(e,t){if(!e)return[];let n=[];for(let o of At){let i=e[o],s=t[o];Pe(i)||Pe(s)||je(i)!==je(s)&&n.push(o)}return n}function Mt(e){let t=new Set(["seenCount","sources","firstSeen","lastSeen","lastUpdated","encounters","jobIds","lastFullVisitAt","lastSnapshotAt","messageSent","connectionStatus","connectionDate","connectionMethod"]),n={};for(let[o,i]of Object.entries(e))t.has(o)||(n[o]=i);return n}function me(){return W||(qe(),W=Ue(de,"people.json")||{},W)}function We(e){qe(),W=e;try{Be(de,e)}catch(t){console.error(`[!] Failed to save people.json: ${t.message}`),console.error(` Path: ${b.resolve(de)}`)}}function pe(e,t={}){if(!e)return null;let n=me(),o=Et(),i=new Date().toISOString(),s=He(n,e,t.name),r=n[s];if(r){let y=Lt(r,t);y.length>0&&(Dt(s,{capturedAt:i,previousLastUpdated:r.lastUpdated||null,changedFields:y,snapshot:Mt(r)}),r.lastSnapshotAt=i),r.seenCount=(r.seenCount||1)+1,r.lastSeen=o,r.lastUpdated=i,t.source&&!r.sources?.includes(t.source)&&(r.sources=r.sources||[],r.sources.push(t.source));for(let m of It)if(t[m]!=null){if(Array.isArray(t[m])&&t[m].length===0&&Array.isArray(r[m])&&r[m].length>0)continue;r[m]=t[m]}Array.isArray(t.availableSections)&&(r.lastFullVisitAt=i)}else n[s]={name:t.name||null,title:t.title||null,location:t.location||null,company:t.company||null,about:t.about||null,connectionDegree:t.connectionDegree||null,connectionsCount:t.connectionsCount||null,mutualConnections:t.mutualConnections||null,photoUrl:t.photoUrl||null,hasVerifiedBadge:t.hasVerifiedBadge||!1,currentCompany:t.currentCompany||null,currentSchool:t.currentSchool||null,connectionsLevel:t.connectionsLevel||null,mutualConnectionsCount:t.mutualConnectionsCount||null,followers:t.followers||null,openToWork:t.openToWork||!1,hiring:t.hiring||!1,topSkills:t.topSkills||null,availableSections:t.availableSections||null,experience:t.experience||null,featuredLinks:t.featuredLinks||null,_lastEnrichedAt:t._lastEnrichedAt||null,featured:t.featured||null,education:t.education||null,skillsFull:t.skillsFull||null,recommendations:t.recommendations||null,certifications:t.certifications||null,firstSeen:o,lastSeen:o,seenCount:1,sources:t.source?[t.source]:[],connectionStatus:null,requestedConnectionAt:null,connectionDate:null,connectionMethod:null,messageSent:null,jobIds:[],lastUpdated:i,lastSnapshotAt:null,lastFullVisitAt:Array.isArray(t.availableSections)?i:null};return We(n),n[s]}function he(e,t,n={}){if(n.dryRun||!e||!t)return;let o=q(t);if(!o)return;let i=me(),s=He(i,e),r=i[s];if(!r)return;let y=q(r.profileUrl||s);o!==y&&(Array.isArray(r.aliases)||(r.aliases=[]),r.aliases.includes(o)||(r.aliases.push(o),r.lastUpdated=new Date().toISOString(),We(i)))}var vt,xt,_,de,nn,on,rn,sn,ln,fe,Oe,an,cn,W,It,At,we=Q(async()=>{await Te();vt=Ct(import.meta.url),xt=b.dirname(b.dirname(vt)),_=process.env.DATA_DIR?b.resolve(process.env.DATA_DIR,"collected-profiles"):b.join(xt,"collected-profiles"),de=b.join(_,"people.json"),nn=b.join(_,"jobs.json"),on=b.join(_,"quota.json"),rn=b.join(_,"conversations.json"),sn=b.join(_,"sync-state.json"),ln=b.join(_,"application-questions.json"),fe=b.join(_,"profile-history"),Oe=(e,t)=>{let n=parseInt(e,10);return Number.isInteger(n)&&n>=0?n:t},an=Oe(process.env.CONNECT_MAX_DAILY,20),cn=Oe(process.env.CONNECT_MAX_WEEKLY,100),W=null;It=["name","title","location","company","connectionDegree","connectionsCount","mutualConnections","photoUrl","hasVerifiedBadge","about","currentCompany","currentSchool","connectionsLevel","mutualConnectionsCount","followers","openToWork","hiring","topSkills","availableSections","experience","featuredLinks","_lastEnrichedAt","featured","education","skillsFull","recommendations","certifications"],At=["name","title","location","currentCompany","currentSchool","connectionDegree","hasVerifiedBadge","openToWork","hiring","about","topSkills"]});var Je={};bt(Je,{extractCertificationsFromDetails:()=>Pt,extractEducationFromDetails:()=>Tt,extractExperienceFromDetails:()=>Nt,extractFeaturedFromDetails:()=>Rt,extractRecommendationsFromDetails:()=>jt,extractSkillsFromDetails:()=>Ft});async function Nt(e,t){let n=t.replace(/\/+$/,"")+"/details/experience/";try{await e.goto(n,{waitUntil:"domcontentloaded",timeout:6e4}),await g(3e3);let o=e.url();if(/\/(login|authwall|checkpoint)/i.test(o))throw new Error(`Session expired \u2014 bounced to ${o} when fetching /details/experience/`);for(let s=0;s<5;s++)await e.evaluate(()=>window.scrollTo(0,document.body.scrollHeight)),await g(900);await g(1e3);let i=0;for(;i<5&&await e.evaluate(()=>{for(let r of document.querySelectorAll("button"))if(/^Load more$/i.test((r.textContent||"").trim()))return r.click(),!0;return!1});)i++,await g(1500),await e.evaluate(()=>window.scrollTo(0,document.body.scrollHeight)),await g(700);return await e.evaluate(()=>{let s=document.querySelector("main");if(!s)return[];function r(c){let k=[];for(let h of c.querySelectorAll("div")){let w=Array.from(h.children).filter(E=>E.tagName==="DIV");if(w.length<3)continue;let S=w.filter(E=>(E.textContent||"").trim().length>50);S.length<3||(h.textContent||"").length<500||k.push({el:h,count:S.length,totalText:(h.textContent||"").length})}return k.sort((h,w)=>w.count-h.count||h.totalText-w.totalText),k[0]?.el}let y=r(s);if(!y)return[];let m=Array.from(y.children).filter(c=>c.tagName==="DIV").filter(c=>(c.textContent||"").trim().length>50);function $(c){let k=c[0]||null,h=null,w=null;if(c[1]){let d=c[1].split("\xB7").map(C=>C.trim());h=d[0]||null,d[1]&&(w=d[1])}let S=c.findIndex(d=>/(\d{4}|present)/i.test(d)&&/(-|–|—)/i.test(d)&&d.length<80),E=null,R=null;if(S>=0){let d=c[S].split("\xB7").map(C=>C.trim());E=d[0]||null,d[1]&&(R=d[1])}let T=null,F=-1;if(S>=0&&c[S+1]){let d=c[S+1],C=/^[>*\-•]|^&gt;/.test(d),U=/https?:\/\//.test(d),H=/\d{4}/.test(d),v=/,/.test(d)||/\bArea\b|\bRegion\b|\bProvince\b|\bCountry\b|, [A-Z]{2}\b/.test(d);!C&&!U&&!H&&d.length<100&&v&&(T=d,F=S+1)}let le=null,p=-1,P=c[c.length-1];if(P&&/and\s+\+\d+\s+skills?$/i.test(P)){p=c.length-1;let d=P.match(/^(.+?)\s+and\s+\+(\d+)\s+skills?$/i);d&&(le={listed:d[1].split(",").map(C=>C.trim()),additional:parseInt(d[2],10)})}let j=new Set([0,1,S,F,p].filter(d=>d>=0)),D=c.filter((d,C)=>!j.has(C)),x=D.length>0?D.join(`
2
2
 
3
3
  `):null;return{title:k,company:h,employmentType:w,dates:E,duration:R,location:T,description:x,skills:le,_raw:c}}return m.map(c=>{let h=(c.innerText||"").trim().split(`
4
- `).map(w=>w.trim()).filter(w=>w&&w.length<5e3);return $(h)}).filter(c=>c.title)})}catch(o){if(/session expired|bounced to|authwall|checkpoint/i.test(o.message||""))throw o;return console.error(`extractExperienceFromDetails failed for ${t}: ${o.message?.slice(0,120)}`),[]}}async function K(e,t,n){let o=t.replace(/\/+$/,"")+`/details/${n}/`;await e.goto(o,{waitUntil:"domcontentloaded",timeout:6e4}),await g(3e3);let i=e.url();if(/\/(login|authwall|checkpoint)/i.test(i))throw new Error(`Session expired \u2014 bounced to ${i} when fetching /details/${n}/`);if(!i.toLowerCase().includes(`/details/${n.toLowerCase()}/`))throw new Error(`Section /details/${n}/ not available on profile (landed on ${i})`);for(let r=0;r<4;r++)await e.evaluate(()=>window.scrollTo(0,document.body.scrollHeight)),await g(700);await g(800);let s=0;for(;s<5&&await e.evaluate(()=>{for(let y of document.querySelectorAll("button"))if(/^Load more$/i.test((y.textContent||"").trim()))return y.click(),!0;return!1});)s++,await g(1300),await e.evaluate(()=>window.scrollTo(0,document.body.scrollHeight)),await g(600)}async function Y(e,t,n,o){try{return await o()}catch(i){let s=i.message||"";if(/session expired|bounced to|authwall|checkpoint/i.test(s))throw i;return/not available on profile/i.test(s)?(console.log(` (${t}: section not on profile \u2014 skipped)`),[]):(console.error(`${n} failed for ${e}: ${s.slice(0,120)}`),[])}}async function Rt(e,t){return Y(t,"featured","extractFeaturedFromDetails",async()=>(await K(e,t,"featured"),await e.evaluate(`(() => {
5
- ${J}
4
+ `).map(w=>w.trim()).filter(w=>w&&w.length<5e3);return $(h)}).filter(c=>c.title)})}catch(o){if(/session expired|bounced to|authwall|checkpoint/i.test(o.message||""))throw o;return console.error(`extractExperienceFromDetails failed for ${t}: ${o.message?.slice(0,120)}`),[]}}async function J(e,t,n){let o=t.replace(/\/+$/,"")+`/details/${n}/`;await e.goto(o,{waitUntil:"domcontentloaded",timeout:6e4}),await g(3e3);let i=e.url();if(/\/(login|authwall|checkpoint)/i.test(i))throw new Error(`Session expired \u2014 bounced to ${i} when fetching /details/${n}/`);if(!i.toLowerCase().includes(`/details/${n.toLowerCase()}/`))throw new Error(`Section /details/${n}/ not available on profile (landed on ${i})`);for(let r=0;r<4;r++)await e.evaluate(()=>window.scrollTo(0,document.body.scrollHeight)),await g(700);await g(800);let s=0;for(;s<5&&await e.evaluate(()=>{for(let y of document.querySelectorAll("button"))if(/^Load more$/i.test((y.textContent||"").trim()))return y.click(),!0;return!1});)s++,await g(1300),await e.evaluate(()=>window.scrollTo(0,document.body.scrollHeight)),await g(600)}async function Y(e,t,n,o){try{return await o()}catch(i){let s=i.message||"";if(/session expired|bounced to|authwall|checkpoint/i.test(s))throw i;return/not available on profile/i.test(s)?(console.log(` (${t}: section not on profile \u2014 skipped)`),[]):(console.error(`${n} failed for ${e}: ${s.slice(0,120)}`),[])}}async function Rt(e,t){return Y(t,"featured","extractFeaturedFromDetails",async()=>(await J(e,t,"featured"),await e.evaluate(`(() => {
5
+ ${K}
6
6
 
7
7
  const main = document.querySelector('main');
8
8
  if (!main) return [];
@@ -42,8 +42,8 @@ var yt=Object.defineProperty;var Q=(e,t)=>()=>(e&&(t=e(e=0)),t);var bt=(e,t)=>{f
42
42
  if (e.caption && /^\xB7\\s*\\d+(?:st|nd|rd|th)\\+?$/i.test(e.caption.trim())) return false;
43
43
  return true;
44
44
  });
45
- })()`)))}async function Tt(e,t){return Y(t,"education","extractEducationFromDetails",async()=>(await K(e,t,"education"),await e.evaluate(`(() => {
46
- ${J}
45
+ })()`)))}async function Tt(e,t){return Y(t,"education","extractEducationFromDetails",async()=>(await J(e,t,"education"),await e.evaluate(`(() => {
46
+ ${K}
47
47
 
48
48
  const main = document.querySelector('main');
49
49
  if (!main) return [];
@@ -79,8 +79,8 @@ var yt=Object.defineProperty;var Q=(e,t)=>()=>(e&&(t=e(e=0)),t);var bt=(e,t)=>{f
79
79
  if (e.degree && /^\xB7\\s*\\d+(?:st|nd|rd|th)\\+?$/i.test(e.degree.trim())) return false;
80
80
  return true;
81
81
  });
82
- })()`)))}async function Ft(e,t){return Y(t,"skills","extractSkillsFromDetails",async()=>(await K(e,t,"skills"),await e.evaluate(`(() => {
83
- ${J}
82
+ })()`)))}async function Ft(e,t){return Y(t,"skills","extractSkillsFromDetails",async()=>(await J(e,t,"skills"),await e.evaluate(`(() => {
83
+ ${K}
84
84
 
85
85
  const main = document.querySelector('main');
86
86
  if (!main) return [];
@@ -111,8 +111,8 @@ var yt=Object.defineProperty;var Q=(e,t)=>()=>(e&&(t=e(e=0)),t);var bt=(e,t)=>{f
111
111
  skills.push({ name, endorsements, _raw: lines });
112
112
  }
113
113
  return skills;
114
- })()`)))}async function Pt(e,t){return Y(t,"certifications","extractCertificationsFromDetails",async()=>(await K(e,t,"certifications"),await e.evaluate(`(() => {
115
- ${J}
114
+ })()`)))}async function Pt(e,t){return Y(t,"certifications","extractCertificationsFromDetails",async()=>(await J(e,t,"certifications"),await e.evaluate(`(() => {
115
+ ${K}
116
116
 
117
117
  const main = document.querySelector('main');
118
118
  if (!main) return [];
@@ -136,8 +136,8 @@ var yt=Object.defineProperty;var Q=(e,t)=>()=>(e&&(t=e(e=0)),t);var bt=(e,t)=>{f
136
136
 
137
137
  return { name, issuer, issueDate, expiryDate, credentialId, _raw: lines };
138
138
  }).filter(e => e.name);
139
- })()`)))}async function jt(e,t){return Y(t,"recommendations","extractRecommendationsFromDetails",async()=>(await K(e,t,"recommendations"),await e.evaluate(`(() => {
140
- ${J}
139
+ })()`)))}async function jt(e,t){return Y(t,"recommendations","extractRecommendationsFromDetails",async()=>(await J(e,t,"recommendations"),await e.evaluate(`(() => {
140
+ ${K}
141
141
 
142
142
  const main = document.querySelector('main');
143
143
  if (!main) return [];
@@ -172,7 +172,7 @@ var yt=Object.defineProperty;var Q=(e,t)=>()=>(e&&(t=e(e=0)),t);var bt=(e,t)=>{f
172
172
  if (e.recommenderTitle && /^\xB7\\s*\\d+(?:st|nd|rd|th)\\+?$/i.test(e.recommenderTitle.trim())) return false;
173
173
  return true;
174
174
  });
175
- })()`)))}var J,Je=Q(()=>{ue();J=`
175
+ })()`)))}var K,Ke=Q(()=>{ue();K=`
176
176
  function findEntryContainer(root) {
177
177
  const candidates = [];
178
178
  for (const div of root.querySelectorAll('div')) {
@@ -235,7 +235,7 @@ function findEntryContainer(root) {
235
235
  candidates.sort((a, b) => b.count - a.count || b.totalText - a.totalText);
236
236
  return candidates[0]?.el;
237
237
  }
238
- `});import"dotenv/config";import Vt from"puppeteer";ue();await we();async function Ot(e,t){let n=e.url();if(n.includes("/in/"))return!1;if(n.includes("/login")||n.includes("/authwall")||n.includes("/checkpoint"))throw new Error("Session expired \u2014 redirected to login. Run: ilml linkedin login");console.log(` Off-track: ${n.slice(0,80)}`),console.log(" Navigating back to profile..."),await e.goto(t,{waitUntil:"domcontentloaded",timeout:6e4}),await g(3e3);let o=e.url();return o.includes("/in/")||console.log(` Recovery failed \u2014 still on: ${o.slice(0,80)}`),!0}async function Ye(e,t,n={}){let{extractExperience:o=!1,extractFeatured:i=!1,extractEducation:s=!1,extractSkills:r=!1,extractRecommendations:y=!1,extractCertifications:m=!1}=n;try{await e.goto(t,{waitUntil:"domcontentloaded",timeout:6e4}),await g(3e3),await Re(e)&&(console.log(" Dismissed Premium overlay"),await g(1500),await Ot(e,t));let c=0;for(let p=0;p<2;p++){let P=await e.evaluate(()=>document.body.scrollHeight);for(let D=1;D<=6;D++)await e.evaluate(x=>window.scrollTo(0,x),P*D/6),await g(900);let j=await e.evaluate(()=>document.body.scrollHeight);if(j===c)break;c=j}await g(800);let k=null;try{let p=e.url();p&&/\/in\/[^/?#]/.test(p)&&(k=p.split("?")[0].replace(/\/+$/,""))}catch{}let h=await e.evaluate(()=>{let p=new Set(["Highlights","About","Activity","Experience","Education","Skills","Recommendations","Languages","Interests","Volunteer experience","Featured","Honors & awards","Honors and awards","Projects","Publications","Patents","Certifications","Licenses & certifications","Courses","Test scores","Organizations"]),P=new Set(["Ad Options","Don\u2019t want to see this","0 notifications","Explore Premium profiles","You might like","People you may know","Suggested for you","More profiles for you","Promoted"]),j=l=>{let a=(l||"").trim().replace(/^[·•]\s*/,"");return/^(1st|2nd|3rd|she\/her|he\/him|they\/them|their\/them)$/i.test(a)},D={name:null,title:null,location:null,currentCompany:null,currentSchool:null,connectionDegree:null,hasVerifiedBadge:!1,photoUrl:null,connectionsLevel:null,mutualConnectionsCount:null,followers:null,openToWork:!1,hiring:!1,about:null,topSkills:null,availableSections:[]},x=document.querySelector("main");if(!x)return D;let d=[...Array.from(x.querySelectorAll("h1")),...Array.from(x.querySelectorAll("h2"))],C=Array.from(x.querySelectorAll("h2")),U=d.find(l=>{let a=l.textContent&&l.textContent.trim(),u=(a||"").replace(/\s*\(\d+\)$/,"").trim();return a&&a.length>1&&a.length<100&&!p.has(u)&&!P.has(u)}),H=U?U.textContent.trim():null,v=U?U.parentElement:null;for(;v&&v!==x&&!Array.from(v.children).filter(a=>a.tagName==="P").some(a=>{let u=a.textContent&&a.textContent.trim();return u&&u.length>5&&!j(u)});)v=v.parentElement;let Se=null,ke=null,Ce=null,ae=null;if(v&&v!==x){let l=Array.from(v.children).filter(u=>u.tagName==="P").filter(u=>{let L=u.textContent&&u.textContent.trim();return L&&L.length>5&&!j(L)});if(l[0]&&(Se=l[0].textContent.trim()),l[1]){let u=l[1].textContent.trim().split(/\s*[·•]\s*/);ke=u[0]||null,u.length>1&&(Ce=u.slice(1).join(" \xB7 "))}let a=Array.from(v.children).filter(u=>u.tagName==="DIV");for(let u=a.length-1;u>=0;u--){let L=a[u].textContent&&a[u].textContent.trim();if(L&&!(H&&L.startsWith(H))&&(ae=L.split(/[·•]/)[0].trim(),ae))break}}let ve=null;if(v){let l=v.querySelectorAll("p, span, div");for(let a of l){let u=(a.textContent||"").trim().replace(/^[·•]\s*/,"");if(u==="1st"||u==="2nd"||u==="3rd"){ve=u;break}}}let dt=!!x.querySelector('svg[data-test-icon="verified"], [aria-label*="Verified" i]'),xe=document.querySelector('main img[width="200"], button img[alt*="photo" i]'),ft=xe&&xe.src||null,G=x.textContent||"",Ee=null,Ie=G.match(/(\d+\+?)\s+connections\b/i);Ie&&(Ee=Ie[1]+" connections");let z=null,Ae=G.match(/(\d+)\s+mutual connection/i),_e=G.match(/and\s+(\d+)\s+other mutual/i),mt=G.match(/([A-Z][\w'-]+(?:\s+[A-Z][\w'-]+)?)\s+and\s+([A-Z][\w'-]+(?:\s+[A-Z][\w'-]+)?)\s+are\s+mutual/);Ae?z=parseInt(Ae[1],10):_e?z=2+parseInt(_e[1],10):mt&&(z=2);let $e=null,De=null,Le=C.find(l=>l.textContent&&l.textContent.trim()==="About");if(Le){let l=Le.parentElement;for(;l&&l.tagName!=="SECTION";)l=l.parentElement;if(l){let a=(l.textContent||"").trim();a=a.replace(/^About\s*/,"").trim();let u=a.match(/Top skills\s*(.+?)$/i);if(u){let ce=u[1].split(/\b(?:see (?:more|less)|show (?:more|less))\b/i)[0].trim().split(/\s*[·•]\s*/).map(gt=>gt.trim()).filter(Boolean);ce.length>0&&ce.length<10&&(De=ce)}a=a.split(/\n?…\s*more/i)[0].trim(),a=a.split(/\n?\.\.\.\s*more/i)[0].trim(),a=a.split(/\bTop skills\b/i)[0].trim(),a=a.split(/\b(?:see (?:more|less)|show (?:more|less))\b/i)[0].trim(),$e=a||null}}let Ne=null,Me=C.find(l=>l.textContent&&l.textContent.trim()==="Activity");if(Me){let l=Me.parentElement;for(;l&&l.tagName!=="SECTION";)l=l.parentElement;if(l){let a=(l.textContent||"").slice(0,200).match(/([\d,]+)\s+followers/i);a&&(Ne=parseInt(a[1].replace(/,/g,""),10))}}let pt=[...new Set(C.map(l=>(l.textContent||"").trim().replace(/\s*\(\d+\)$/,"").trim()).filter(l=>l&&l.length>1&&l.length<60&&l!==H).filter(l=>p.has(l)))],V=document.body.textContent||"",ht=/#OpenToWork\b/i.test(V)||/\bOpen to work\b/.test(V),wt=/#Hiring\b/i.test(V)||/\bI'?m hiring\b/i.test(V)||/\bWe are hiring\b/i.test(V);return{name:H,title:Se,location:ae,currentCompany:ke,currentSchool:Ce,connectionDegree:ve,hasVerifiedBadge:dt,photoUrl:ft,connectionsLevel:Ee,mutualConnectionsCount:z,followers:Ne,openToWork:ht,hiring:wt,about:$e,topSkills:De,availableSections:pt}});console.log(`Viewed: ${h.name} - ${h.title}`);let w=null,S=null,E=null,R=null,T=null,F=null;if(o||i||s||r||y||m){let p=await Promise.resolve().then(()=>(Je(),Ke));o&&(w=await p.extractExperienceFromDetails(e,t),console.log(` + Experience: ${w.length} entries`)),i&&(S=await p.extractFeaturedFromDetails(e,t),console.log(` + Featured: ${S.length} entries`)),s&&(E=await p.extractEducationFromDetails(e,t),console.log(` + Education: ${E.length} entries`)),r&&(R=await p.extractSkillsFromDetails(e,t),console.log(` + Skills: ${R.length} entries`)),y&&(T=await p.extractRecommendationsFromDetails(e,t),console.log(` + Recommendations: ${T.length} entries`)),m&&(F=await p.extractCertificationsFromDetails(e,t),console.log(` + Certifications: ${F.length} entries`))}return{...h,profileUrl:t,resolvedUrl:k,...w!==null&&{experience:w},...S!==null&&{featured:S},...E!==null&&{education:E},...R!==null&&{skillsFull:R},...T!==null&&{recommendations:T},...F!==null&&{certifications:F}}}catch($){console.error(`Error viewing profile ${t}:`,$.message);try{await e.goto("about:blank",{timeout:1e4}),await g(1e3)}catch{}if(/session expired|bounced to|authwall|checkpoint|redirected to login/i.test($.message||""))throw $;return null}}await we();import I from"fs";import N from"path";var qt="cookies.json";function Ge(){let e=process.env.ILML_SCOPE_DIR;return e?N.join(e,"plugins-state","linkedin","cookies.json"):qt}function ze(){let e=Ge();if(!I.existsSync(e))throw new Error(`Cookies not found at ${e}. Run: ilml linkedin login`);let t;try{t=I.readFileSync(e,"utf-8")}catch(o){throw new Error(`Failed to read cookies at ${e}: ${o.message}. Run: ilml linkedin login`)}let n;try{n=JSON.parse(t)}catch{throw new Error(`Cookies file at ${e} is corrupted (invalid JSON). Run: ilml linkedin login`)}if(!Array.isArray(n)||n.length===0)throw new Error(`Cookies file at ${e} is empty or not an array. Run: ilml linkedin login`);return n}function Qe(){return I.existsSync(Ge())}function Bt(){let e=process.env.ILML_SCOPE_DIR;return e?N.join(e,"plugins-state","linkedin"):"."}function Ze(e){let t=String(e||"").replace(/[^a-zA-Z0-9_.-]/g,"_").slice(0,80);return/[a-zA-Z0-9]/.test(t)?t:"default"}function Ut(){let e=process.env.MY_LINKEDIN_URL,t=e&&e.match(/\/in\/([^/?#]+)/);return t&&t[1]?Ze(t[1]):"default"}function Xe(e){let t=process.env.LINKEDIN_CHROME_PROFILE_DIR;if(t)return I.existsSync(t)||I.mkdirSync(t,{recursive:!0}),N.resolve(t);let n=Ze(e||Ut()),o=N.join(Bt(),"chrome-profile",n);return I.existsSync(o)||I.mkdirSync(o,{recursive:!0}),N.resolve(o)}var et=".ilml-seeded";function tt(e){return!I.existsSync(N.join(e,et))}function nt(e){try{I.writeFileSync(N.join(e,et),new Date().toISOString())}catch{}}function ot(e){let t=0;for(let n of["SingletonLock","SingletonCookie","SingletonSocket"])try{I.rmSync(N.join(e,n),{force:!0}),t++}catch{}return t}var Ht={headless:!1,defaultViewport:null,protocolTimeout:3e5};async function it(e,t={}){let n=Xe(),o={...Ht,...t,userDataDir:n},i;try{i=await e.launch(o)}catch{ot(n);try{i=await e.launch(o)}catch(r){throw new Error(`Could not open the LinkedIn automation Chrome profile at ${n}. If a LinkedIn automation window is already open, close it and retry. (Chrome: ${r.message})`)}}return{browser:i,profileDir:n,needsSeed:tt(n)}}async function rt(e,t){if(!t||!t.needsSeed)return!1;if(!Qe())return console.warn(" \u26A0 No saved LinkedIn session found. If this command comes back empty or redirects to login, run: ilml linkedin login"),!1;try{return await e.setCookie(...ze()),nt(t.profileDir),console.log(" (seeded persistent profile from saved cookies \u2014 one-time)"),!0}catch{return!1}}function st(e){return e?e.replace(/^https?:\/\/(www\.|m\.)?linkedin\.com/i,"https://linkedin.com").split("?")[0].split("#")[0].replace(/\/+$/,"").toLowerCase():""}function lt(e){return(e||"").replace(/ /g," ").replace(/\s+/g," ").trim().toLowerCase()}var A=process.argv.slice(2),Wt=A.includes("--help")||A.includes("-h"),M=A.find(e=>e.startsWith("http")),B=A.includes("--full"),X=B||A.includes("--with-experience"),ee=B||A.includes("--with-featured"),te=B||A.includes("--with-education"),ne=B||A.includes("--with-skills"),oe=B||A.includes("--with-recommendations"),ie=B||A.includes("--with-certifications"),at=[["experience",X],["featured",ee],["education",te],["skills",ne],["recommendations",oe],["certifications",ie]],ct=at.filter(([,e])=>e).length,ye=1+ct;function ut(){console.log(`
238
+ `});import"dotenv/config";import Vt from"puppeteer";ue();await we();async function Ot(e,t){let n=e.url();if(n.includes("/in/"))return!1;if(n.includes("/login")||n.includes("/authwall")||n.includes("/checkpoint"))throw new Error("Session expired \u2014 redirected to login. Run: ilml linkedin login");console.log(` Off-track: ${n.slice(0,80)}`),console.log(" Navigating back to profile..."),await e.goto(t,{waitUntil:"domcontentloaded",timeout:6e4}),await g(3e3);let o=e.url();return o.includes("/in/")||console.log(` Recovery failed \u2014 still on: ${o.slice(0,80)}`),!0}async function Ye(e,t,n={}){let{extractExperience:o=!1,extractFeatured:i=!1,extractEducation:s=!1,extractSkills:r=!1,extractRecommendations:y=!1,extractCertifications:m=!1}=n;try{await e.goto(t,{waitUntil:"domcontentloaded",timeout:6e4}),await g(3e3),await Re(e)&&(console.log(" Dismissed Premium overlay"),await g(1500),await Ot(e,t));let c=0;for(let p=0;p<2;p++){let P=await e.evaluate(()=>document.body.scrollHeight);for(let D=1;D<=6;D++)await e.evaluate(x=>window.scrollTo(0,x),P*D/6),await g(900);let j=await e.evaluate(()=>document.body.scrollHeight);if(j===c)break;c=j}await g(800);let k=null;try{let p=e.url();p&&/\/in\/[^/?#]/.test(p)&&(k=p.split("?")[0].replace(/\/+$/,""))}catch{}let h=await e.evaluate(()=>{let p=new Set(["Highlights","About","Activity","Experience","Education","Skills","Recommendations","Languages","Interests","Volunteer experience","Featured","Honors & awards","Honors and awards","Projects","Publications","Patents","Certifications","Licenses & certifications","Courses","Test scores","Organizations"]),P=new Set(["Ad Options","Don\u2019t want to see this","0 notifications","Explore Premium profiles","You might like","People you may know","Suggested for you","More profiles for you","Promoted"]),j=l=>{let a=(l||"").trim().replace(/^[·•]\s*/,"");return/^(1st|2nd|3rd|she\/her|he\/him|they\/them|their\/them)$/i.test(a)},D={name:null,title:null,location:null,currentCompany:null,currentSchool:null,connectionDegree:null,hasVerifiedBadge:!1,photoUrl:null,connectionsLevel:null,mutualConnectionsCount:null,followers:null,openToWork:!1,hiring:!1,about:null,topSkills:null,availableSections:[]},x=document.querySelector("main");if(!x)return D;let d=[...Array.from(x.querySelectorAll("h1")),...Array.from(x.querySelectorAll("h2"))],C=Array.from(x.querySelectorAll("h2")),U=d.find(l=>{let a=l.textContent&&l.textContent.trim(),u=(a||"").replace(/\s*\(\d+\)$/,"").trim();return a&&a.length>1&&a.length<100&&!p.has(u)&&!P.has(u)}),H=U?U.textContent.trim():null,v=U?U.parentElement:null;for(;v&&v!==x&&!Array.from(v.children).filter(a=>a.tagName==="P").some(a=>{let u=a.textContent&&a.textContent.trim();return u&&u.length>5&&!j(u)});)v=v.parentElement;let Se=null,ke=null,Ce=null,ae=null;if(v&&v!==x){let l=Array.from(v.children).filter(u=>u.tagName==="P").filter(u=>{let L=u.textContent&&u.textContent.trim();return L&&L.length>5&&!j(L)});if(l[0]&&(Se=l[0].textContent.trim()),l[1]){let u=l[1].textContent.trim().split(/\s*[·•]\s*/);ke=u[0]||null,u.length>1&&(Ce=u.slice(1).join(" \xB7 "))}let a=Array.from(v.children).filter(u=>u.tagName==="DIV");for(let u=a.length-1;u>=0;u--){let L=a[u].textContent&&a[u].textContent.trim();if(L&&!(H&&L.startsWith(H))&&(ae=L.split(/[·•]/)[0].trim(),ae))break}}let ve=null;if(v){let l=v.querySelectorAll("p, span, div");for(let a of l){let u=(a.textContent||"").trim().replace(/^[·•]\s*/,"");if(u==="1st"||u==="2nd"||u==="3rd"){ve=u;break}}}let dt=!!x.querySelector('svg[data-test-icon="verified"], [aria-label*="Verified" i]'),xe=document.querySelector('main img[width="200"], button img[alt*="photo" i]'),ft=xe&&xe.src||null,G=x.textContent||"",Ee=null,Ie=G.match(/(\d+\+?)\s+connections\b/i);Ie&&(Ee=Ie[1]+" connections");let z=null,Ae=G.match(/(\d+)\s+mutual connection/i),_e=G.match(/and\s+(\d+)\s+other mutual/i),mt=G.match(/([A-Z][\w'-]+(?:\s+[A-Z][\w'-]+)?)\s+and\s+([A-Z][\w'-]+(?:\s+[A-Z][\w'-]+)?)\s+are\s+mutual/);Ae?z=parseInt(Ae[1],10):_e?z=2+parseInt(_e[1],10):mt&&(z=2);let $e=null,De=null,Le=C.find(l=>l.textContent&&l.textContent.trim()==="About");if(Le){let l=Le.parentElement;for(;l&&l.tagName!=="SECTION";)l=l.parentElement;if(l){let a=(l.textContent||"").trim();a=a.replace(/^About\s*/,"").trim();let u=a.match(/Top skills\s*(.+?)$/i);if(u){let ce=u[1].split(/\b(?:see (?:more|less)|show (?:more|less))\b/i)[0].trim().split(/\s*[·•]\s*/).map(gt=>gt.trim()).filter(Boolean);ce.length>0&&ce.length<10&&(De=ce)}a=a.split(/\n?…\s*more/i)[0].trim(),a=a.split(/\n?\.\.\.\s*more/i)[0].trim(),a=a.split(/\bTop skills\b/i)[0].trim(),a=a.split(/\b(?:see (?:more|less)|show (?:more|less))\b/i)[0].trim(),$e=a||null}}let Me=null,Ne=C.find(l=>l.textContent&&l.textContent.trim()==="Activity");if(Ne){let l=Ne.parentElement;for(;l&&l.tagName!=="SECTION";)l=l.parentElement;if(l){let a=(l.textContent||"").slice(0,200).match(/([\d,]+)\s+followers/i);a&&(Me=parseInt(a[1].replace(/,/g,""),10))}}let pt=[...new Set(C.map(l=>(l.textContent||"").trim().replace(/\s*\(\d+\)$/,"").trim()).filter(l=>l&&l.length>1&&l.length<60&&l!==H).filter(l=>p.has(l)))],V=document.body.textContent||"",ht=/#OpenToWork\b/i.test(V)||/\bOpen to work\b/.test(V),wt=/#Hiring\b/i.test(V)||/\bI'?m hiring\b/i.test(V)||/\bWe are hiring\b/i.test(V);return{name:H,title:Se,location:ae,currentCompany:ke,currentSchool:Ce,connectionDegree:ve,hasVerifiedBadge:dt,photoUrl:ft,connectionsLevel:Ee,mutualConnectionsCount:z,followers:Me,openToWork:ht,hiring:wt,about:$e,topSkills:De,availableSections:pt}});console.log(`Viewed: ${h.name} - ${h.title}`);let w=null,S=null,E=null,R=null,T=null,F=null;if(o||i||s||r||y||m){let p=await Promise.resolve().then(()=>(Ke(),Je));o&&(w=await p.extractExperienceFromDetails(e,t),console.log(` + Experience: ${w.length} entries`)),i&&(S=await p.extractFeaturedFromDetails(e,t),console.log(` + Featured: ${S.length} entries`)),s&&(E=await p.extractEducationFromDetails(e,t),console.log(` + Education: ${E.length} entries`)),r&&(R=await p.extractSkillsFromDetails(e,t),console.log(` + Skills: ${R.length} entries`)),y&&(T=await p.extractRecommendationsFromDetails(e,t),console.log(` + Recommendations: ${T.length} entries`)),m&&(F=await p.extractCertificationsFromDetails(e,t),console.log(` + Certifications: ${F.length} entries`))}return{...h,profileUrl:t,resolvedUrl:k,...w!==null&&{experience:w},...S!==null&&{featured:S},...E!==null&&{education:E},...R!==null&&{skillsFull:R},...T!==null&&{recommendations:T},...F!==null&&{certifications:F}}}catch($){console.error(`Error viewing profile ${t}:`,$.message);try{await e.goto("about:blank",{timeout:1e4}),await g(1e3)}catch{}if(/session expired|bounced to|authwall|checkpoint|redirected to login/i.test($.message||""))throw $;return null}}await we();import I from"fs";import M from"path";var qt="cookies.json";function Ge(){let e=process.env.ILML_SCOPE_DIR;return e?M.join(e,"plugins-state","linkedin","cookies.json"):qt}function ze(){let e=Ge();if(!I.existsSync(e))throw new Error(`Cookies not found at ${e}. Run: ilml linkedin login`);let t;try{t=I.readFileSync(e,"utf-8")}catch(o){throw new Error(`Failed to read cookies at ${e}: ${o.message}. Run: ilml linkedin login`)}let n;try{n=JSON.parse(t)}catch{throw new Error(`Cookies file at ${e} is corrupted (invalid JSON). Run: ilml linkedin login`)}if(!Array.isArray(n)||n.length===0)throw new Error(`Cookies file at ${e} is empty or not an array. Run: ilml linkedin login`);return n}function Qe(){return I.existsSync(Ge())}function Bt(){let e=process.env.ILML_SCOPE_DIR;return e?M.join(e,"plugins-state","linkedin"):"."}function Ze(e){let t=String(e||"").replace(/[^a-zA-Z0-9_.-]/g,"_").slice(0,80);return/[a-zA-Z0-9]/.test(t)?t:"default"}function Ut(){let e=process.env.MY_LINKEDIN_URL,t=e&&e.match(/\/in\/([^/?#]+)/);return t&&t[1]?Ze(t[1]):"default"}function Xe(e){let t=process.env.LINKEDIN_CHROME_PROFILE_DIR;if(t)return I.existsSync(t)||I.mkdirSync(t,{recursive:!0}),M.resolve(t);let n=Ze(e||Ut()),o=M.join(Bt(),"chrome-profile",n);return I.existsSync(o)||I.mkdirSync(o,{recursive:!0}),M.resolve(o)}var et=".ilml-seeded";function tt(e){return!I.existsSync(M.join(e,et))}function nt(e){try{I.writeFileSync(M.join(e,et),new Date().toISOString())}catch{}}function ot(e){let t=0;for(let n of["SingletonLock","SingletonCookie","SingletonSocket"])try{I.rmSync(M.join(e,n),{force:!0}),t++}catch{}return t}var Ht={headless:!1,defaultViewport:null,protocolTimeout:3e5};async function it(e,t={}){let n=Xe(),o={...Ht,...t,userDataDir:n},i;try{i=await e.launch(o)}catch{ot(n);try{i=await e.launch(o)}catch(r){throw new Error(`Could not open the LinkedIn automation Chrome profile at ${n}. If a LinkedIn automation window is already open, close it and retry. (Chrome: ${r.message})`)}}return{browser:i,profileDir:n,needsSeed:tt(n)}}async function rt(e,t){if(!t||!t.needsSeed)return!1;if(!Qe())return console.warn(" \u26A0 No saved LinkedIn session found. If this command comes back empty or redirects to login, run: ilml linkedin login"),!1;try{return await e.setCookie(...ze()),nt(t.profileDir),console.log(" (seeded persistent profile from saved cookies \u2014 one-time)"),!0}catch{return!1}}function st(e){return e?e.replace(/^https?:\/\/(www\.|m\.)?linkedin\.com/i,"https://linkedin.com").split("?")[0].split("#")[0].replace(/\/+$/,"").toLowerCase():""}function lt(e){return(e||"").replace(/ /g," ").replace(/\s+/g," ").trim().toLowerCase()}var A=process.argv.slice(2),Wt=A.includes("--help")||A.includes("-h"),N=A.find(e=>e.startsWith("http")),B=A.includes("--full"),X=B||A.includes("--with-experience"),ee=B||A.includes("--with-featured"),te=B||A.includes("--with-education"),ne=B||A.includes("--with-skills"),oe=B||A.includes("--with-recommendations"),ie=B||A.includes("--with-certifications"),at=[["experience",X],["featured",ee],["education",te],["skills",ne],["recommendations",oe],["certifications",ie]],ct=at.filter(([,e])=>e).length,ye=1+ct;function ut(){console.log(`
239
239
  enrich-profile \u2014 visit ONE LinkedIn profile and save what we learn
240
240
 
241
241
  USAGE
@@ -286,4 +286,4 @@ WHEN TO USE
286
286
  - Audit your own LinkedIn profile (--full prints everything to stdout)
287
287
  - Deep-dive on one specific contact before reaching out
288
288
  - Verify what we know about a recruiter / lead
289
- `)}Wt&&(ut(),process.exit(0));M||(ut(),process.exit(1));/linkedin\.com\/in\//i.test(M)||(console.error(`URL doesn't look like a LinkedIn profile: ${M}`),console.error("Expected format: https://www.linkedin.com/in/<slug>/"),process.exit(1));var be=st(process.env.MY_LINKEDIN_URL||""),se=lt(process.env.LINKEDIN_NAME||"");!be&&!se&&(console.error(""),console.error(" \u26D4 Refusing to run: cannot self-detect without configuration."),console.error(""),console.error(" Set EITHER (preferably both) of:"),console.error(" MY_LINKEDIN_URL=https://www.linkedin.com/in/<your-slug>"),console.error(' LINKEDIN_NAME="Your Full Name"'),console.error(""),console.error(" in .env (dev) or via `ilml plugin config linkedin set <KEY> <VALUE>` (prod)."),console.error(" This prevents your own profile from accidentally landing in people.json."),console.error(""),process.exit(1));var re=be&&st(M)===be,ge=at.filter(([,e])=>e).map(([e])=>e);console.log("");console.log(` enrichProfile \u2014 ${M}`);B?console.log(` Mode: --full (${ye} page loads: main + ${ge.join(" + ")})`):ge.length>0?console.log(` Mode: rich (${ye} loads: main + ${ge.join(" + ")})`):console.log(" Mode: basic (1 load \u2014 main page only)");console.log(re?" Target: SELF (URL match) \u2014 will print to stdout, not saved to people.json":se?` Target: TBD \u2014 name-match against "${process.env.LINKEDIN_NAME}" after fetch`:" Target: will save to people.json (no MY_LINKEDIN_URL or LINKEDIN_NAME set for self-check)");ct>0&&(console.log(""),console.log(` \u26A0 ${ye} page loads on the SAME profile = normal "deep read" pattern.`),console.log(" This is intentionally single-URL \u2014 DO NOT batch. For batch enrichment"),console.log(" use `enrichProfiles.mjs --with-experience` (gated, capped)."));console.log("");var Z=null;try{let e=await it(Vt);Z=e.browser;let t=await Z.newPage();await rt(t,e);let n=await Ye(t,M,{extractExperience:X,extractFeatured:ee,extractEducation:te,extractSkills:ne,extractRecommendations:oe,extractCertifications:ie});if((!n||!n.name)&&(console.error(" viewProfile returned no name \u2014 profile not loaded or DOM changed"),process.exit(1)),!re&&se&&n.name&&lt(n.name)===se&&(re=!0,console.log(" \u2191 Detected self via LINKEDIN_NAME match \u2014 will NOT save to people.json")),console.log(""),console.log(" \u2500\u2500\u2500 extracted \u2500\u2500\u2500"),console.log(` name: ${n.name}`),console.log(` title: ${n.title||"(none)"}`),console.log(` location: ${n.location||"(none)"}`),console.log(` about: ${n.about?`${n.about.length} chars`:"(none)"}`),console.log(` topSkills: ${n.topSkills?n.topSkills.join(", "):"(none)"}`),X&&console.log(` experience: ${(n.experience||[]).length} entries`),ee&&console.log(` featured: ${(n.featured||[]).length} entries`),te&&console.log(` education: ${(n.education||[]).length} entries`),ne&&console.log(` skillsFull: ${(n.skillsFull||[]).length} entries`),oe&&console.log(` recommendations: ${(n.recommendations||[]).length} entries`),ie&&console.log(` certifications: ${(n.certifications||[]).length} entries`),console.log(""),re)console.log(" \u2500\u2500\u2500 full data (self \u2014 not persisted) \u2500\u2500\u2500"),console.log(JSON.stringify(n,null,2));else{let o={name:n.name,title:n.title,location:n.location,about:n.about,currentCompany:n.currentCompany,currentSchool:n.currentSchool,topSkills:n.topSkills,availableSections:n.availableSections,connectionsLevel:n.connectionsLevel,mutualConnectionsCount:n.mutualConnectionsCount,followers:n.followers,openToWork:n.openToWork,hiring:n.hiring,hasVerifiedBadge:n.hasVerifiedBadge,photoUrl:n.photoUrl,source:"enrich-profile-single"},i=[["experience",X,n.experience],["featured",ee,n.featured],["education",te,n.education],["skillsFull",ne,n.skillsFull],["recommendations",oe,n.recommendations],["certifications",ie,n.certifications]],s=!1;for(let[r,y,m]of i)y&&(Array.isArray(m)&&m.length>0?(o[r]=m,s=!0):console.log(` \u26A0 --with-${r==="skillsFull"?"skills":r} returned 0 entries \u2014 NOT writing ${r} (existing data preserved)`));s&&(o._lastEnrichedAt=new Date().toISOString()),pe(M,o),n.resolvedUrl&&he(M,n.resolvedUrl),console.log(" \u2713 Saved to people.json")}}catch(e){console.error(" Error:",e.message),process.exit(1)}finally{Z&&await Z.close().catch(()=>{})}
289
+ `)}Wt&&(ut(),process.exit(0));N||(ut(),process.exit(1));/linkedin\.com\/in\//i.test(N)||(console.error(`URL doesn't look like a LinkedIn profile: ${N}`),console.error("Expected format: https://www.linkedin.com/in/<slug>/"),process.exit(1));var be=st(process.env.MY_LINKEDIN_URL||""),se=lt(process.env.LINKEDIN_NAME||"");!be&&!se&&(console.error(""),console.error(" \u26D4 Refusing to run: cannot self-detect without configuration."),console.error(""),console.error(" Set EITHER (preferably both) of:"),console.error(" MY_LINKEDIN_URL=https://www.linkedin.com/in/<your-slug>"),console.error(' LINKEDIN_NAME="Your Full Name"'),console.error(""),console.error(" in .env (dev) or via `ilml plugin config linkedin set <KEY> <VALUE>` (prod)."),console.error(" This prevents your own profile from accidentally landing in people.json."),console.error(""),process.exit(1));var re=be&&st(N)===be,ge=at.filter(([,e])=>e).map(([e])=>e);console.log("");console.log(` enrichProfile \u2014 ${N}`);B?console.log(` Mode: --full (${ye} page loads: main + ${ge.join(" + ")})`):ge.length>0?console.log(` Mode: rich (${ye} loads: main + ${ge.join(" + ")})`):console.log(" Mode: basic (1 load \u2014 main page only)");console.log(re?" Target: SELF (URL match) \u2014 will print to stdout, not saved to people.json":se?` Target: TBD \u2014 name-match against "${process.env.LINKEDIN_NAME}" after fetch`:" Target: will save to people.json (no MY_LINKEDIN_URL or LINKEDIN_NAME set for self-check)");ct>0&&(console.log(""),console.log(` \u26A0 ${ye} page loads on the SAME profile = normal "deep read" pattern.`),console.log(" This is intentionally single-URL \u2014 DO NOT batch. For batch enrichment"),console.log(" use `enrichProfiles.mjs --with-experience` (gated, capped)."));console.log("");var Z=null;try{let e=await it(Vt);Z=e.browser;let t=await Z.newPage();await rt(t,e);let n=await Ye(t,N,{extractExperience:X,extractFeatured:ee,extractEducation:te,extractSkills:ne,extractRecommendations:oe,extractCertifications:ie});if((!n||!n.name)&&(console.error(" viewProfile returned no name \u2014 profile not loaded or DOM changed"),process.exit(1)),!re&&se&&n.name&&lt(n.name)===se&&(re=!0,console.log(" \u2191 Detected self via LINKEDIN_NAME match \u2014 will NOT save to people.json")),console.log(""),console.log(" \u2500\u2500\u2500 extracted \u2500\u2500\u2500"),console.log(` name: ${n.name}`),console.log(` title: ${n.title||"(none)"}`),console.log(` location: ${n.location||"(none)"}`),console.log(` about: ${n.about?`${n.about.length} chars`:"(none)"}`),console.log(` topSkills: ${n.topSkills?n.topSkills.join(", "):"(none)"}`),X&&console.log(` experience: ${(n.experience||[]).length} entries`),ee&&console.log(` featured: ${(n.featured||[]).length} entries`),te&&console.log(` education: ${(n.education||[]).length} entries`),ne&&console.log(` skillsFull: ${(n.skillsFull||[]).length} entries`),oe&&console.log(` recommendations: ${(n.recommendations||[]).length} entries`),ie&&console.log(` certifications: ${(n.certifications||[]).length} entries`),console.log(""),re)console.log(" \u2500\u2500\u2500 full data (self \u2014 not persisted) \u2500\u2500\u2500"),console.log(JSON.stringify(n,null,2));else{let o={name:n.name,title:n.title,location:n.location,about:n.about,currentCompany:n.currentCompany,currentSchool:n.currentSchool,topSkills:n.topSkills,availableSections:n.availableSections,connectionsLevel:n.connectionsLevel,mutualConnectionsCount:n.mutualConnectionsCount,followers:n.followers,openToWork:n.openToWork,hiring:n.hiring,hasVerifiedBadge:n.hasVerifiedBadge,photoUrl:n.photoUrl,source:"enrich-profile-single"},i=[["experience",X,n.experience],["featured",ee,n.featured],["education",te,n.education],["skillsFull",ne,n.skillsFull],["recommendations",oe,n.recommendations],["certifications",ie,n.certifications]],s=!1;for(let[r,y,m]of i)y&&(Array.isArray(m)&&m.length>0?(o[r]=m,s=!0):console.log(` \u26A0 --with-${r==="skillsFull"?"skills":r} returned 0 entries \u2014 NOT writing ${r} (existing data preserved)`));s&&(o._lastEnrichedAt=new Date().toISOString()),pe(N,o),n.resolvedUrl&&he(N,n.resolvedUrl),console.log(" \u2713 Saved to people.json")}}catch(e){console.error(" Error:",e.message),process.exit(1)}finally{Z&&await Z.close().catch(()=>{})}
@@ -243,7 +243,7 @@ function findEntryContainer(root) {
243
243
 
244
244
  ${o.join(`
245
245
  `)}`;try{await Qt.addMessage(s,l)}catch(a){console.error(`[runReport] Failed to post end-of-session message: ${a.message}`)}}var No=jo(import.meta.url),Oo=ue.dirname(ue.dirname(No)),at=process.env.DATA_DIR?ue.resolve(process.env.DATA_DIR,"collected-profiles"):ue.join(Oo,"collected-profiles"),ct=ue.join(at,"run-log.json");function Fo(e,t){let n=JSON.stringify(t,null,2),o=e+".tmp";J.writeFileSync(o,n);for(let r=0;r<3;r++)try{J.renameSync(o,e);return}catch(i){if(r<2&&(i.code==="EPERM"||i.code==="EBUSY")){let s=Date.now();for(;Date.now()-s<200;);continue}throw i}}function Xt(){try{if(!J.existsSync(ct))return{runs:[]};let e=JSON.parse(J.readFileSync(ct,"utf-8"));return!e.runs||!Array.isArray(e.runs)?{runs:[]}:e}catch{return{runs:[]}}}function en(e){J.existsSync(at)||J.mkdirSync(at,{recursive:!0});try{Fo(ct,e)}catch(t){console.error(`[!] Failed to save run-log.json: ${t.message}`)}}function tn(e,t="default"){let n=Xt();for(let s of n.runs)s.status==="running"&&(s.status="interrupted",s.result="interrupted",s.timestamp&&(s.duration=Date.now()-new Date(s.timestamp).getTime()));let o=new Date,r={timestamp:o.toISOString(),script:e,mode:t,status:"running",result:null,duration:null,stats:null,error:null};n.runs.push(r);let i=Date.now()-30*24*60*60*1e3;return n.runs=n.runs.filter(s=>new Date(s.timestamp).getTime()>i),en(n),{script:e,mode:t,startTime:o.getTime(),runIndex:n.runs.length-1}}function ut(e,t,n=null,o=null){if(!e)return;let r=null;try{let s=Xt(),c=s.runs[e.runIndex];if(c&&c.status==="running"&&c.script===e.script)c.status=t,c.result=t,c.duration=Date.now()-e.startTime,c.stats=n,c.error=o,r=c.duration;else{let l=s.runs.find(a=>a.script===e.script&&a.status==="running"&&a.timestamp===new Date(e.startTime).toISOString());l&&(l.status=t,l.result=t,l.duration=Date.now()-e.startTime,l.stats=n,l.error=o,r=l.duration)}en(s)}catch(s){console.error(`[runLog] Failed to end run: ${s.message}`)}let i=n&&Array.isArray(n.summaryLines)?n.summaryLines:null;if(i&&i.length>0&&process.env.NODE_RUN_REPORTS){let s=t==="error"?"error":n&&n.dailyLimitHit?"quota-reached":"done";Zt({scriptName:e.script,mode:e.mode,status:s,summaryLines:i,error:o,duration:r??Date.now()-e.startTime}).catch(c=>console.error(`[runLog] postRunReport failed: ${c.message}`))}}Ke();Ke();await Xe();async function Hn(e,t){let n=e.url();if(n.includes("/in/"))return!1;if(n.includes("/login")||n.includes("/authwall")||n.includes("/checkpoint"))throw new Error("Session expired \u2014 redirected to login. Run: ilml linkedin login");console.log(` Off-track: ${n.slice(0,80)}`),console.log(" Navigating back to profile..."),await e.goto(t,{waitUntil:"domcontentloaded",timeout:6e4}),await S(3e3);let o=e.url();return o.includes("/in/")||console.log(` Recovery failed \u2014 still on: ${o.slice(0,80)}`),!0}function Vn(e,t=[]){if(!e||!t||t.length===0)return!1;let n=e.toLowerCase();return t.some(o=>n.includes(o.toLowerCase()))}async function Lr(e,t,n={}){let{extractExperience:o=!1,extractFeatured:r=!1,extractEducation:i=!1,extractSkills:s=!1,extractRecommendations:c=!1,extractCertifications:l=!1}=n;try{await e.goto(t,{waitUntil:"domcontentloaded",timeout:6e4}),await S(3e3),await ge(e)&&(console.log(" Dismissed Premium overlay"),await S(1500),await Hn(e,t));let u=0;for(let w=0;w<2;w++){let x=await e.evaluate(()=>document.body.scrollHeight);for(let D=1;D<=6;D++)await e.evaluate(R=>window.scrollTo(0,R),x*D/6),await S(900);let T=await e.evaluate(()=>document.body.scrollHeight);if(T===u)break;u=T}await S(800);let f=null;try{let w=e.url();w&&/\/in\/[^/?#]/.test(w)&&(f=w.split("?")[0].replace(/\/+$/,""))}catch{}let d=await e.evaluate(()=>{let w=new Set(["Highlights","About","Activity","Experience","Education","Skills","Recommendations","Languages","Interests","Volunteer experience","Featured","Honors & awards","Honors and awards","Projects","Publications","Patents","Certifications","Licenses & certifications","Courses","Test scores","Organizations"]),x=new Set(["Ad Options","Don\u2019t want to see this","0 notifications","Explore Premium profiles","You might like","People you may know","Suggested for you","More profiles for you","Promoted"]),T=C=>{let A=(C||"").trim().replace(/^[·•]\s*/,"");return/^(1st|2nd|3rd|she\/her|he\/him|they\/them|their\/them)$/i.test(A)},D={name:null,title:null,location:null,currentCompany:null,currentSchool:null,connectionDegree:null,hasVerifiedBadge:!1,photoUrl:null,connectionsLevel:null,mutualConnectionsCount:null,followers:null,openToWork:!1,hiring:!1,about:null,topSkills:null,availableSections:[]},R=document.querySelector("main");if(!R)return D;let k=[...Array.from(R.querySelectorAll("h1")),...Array.from(R.querySelectorAll("h2"))],_=Array.from(R.querySelectorAll("h2")),X=k.find(C=>{let A=C.textContent&&C.textContent.trim(),E=(A||"").replace(/\s*\(\d+\)$/,"").trim();return A&&A.length>1&&A.length<100&&!w.has(E)&&!x.has(E)}),K=X?X.textContent.trim():null,L=X?X.parentElement:null;for(;L&&L!==R&&!Array.from(L.children).filter(A=>A.tagName==="P").some(A=>{let E=A.textContent&&A.textContent.trim();return E&&E.length>5&&!T(E)});)L=L.parentElement;let le=null,De=null,Re=null,Te=null;if(L&&L!==R){let C=Array.from(L.children).filter(E=>E.tagName==="P").filter(E=>{let ae=E.textContent&&E.textContent.trim();return ae&&ae.length>5&&!T(ae)});if(C[0]&&(le=C[0].textContent.trim()),C[1]){let E=C[1].textContent.trim().split(/\s*[·•]\s*/);De=E[0]||null,E.length>1&&(Re=E.slice(1).join(" \xB7 "))}let A=Array.from(L.children).filter(E=>E.tagName==="DIV");for(let E=A.length-1;E>=0;E--){let ae=A[E].textContent&&A[E].textContent.trim();if(ae&&!(K&&ae.startsWith(K))&&(Te=ae.split(/[·•]/)[0].trim(),Te))break}}let Ge=null;if(L){let C=L.querySelectorAll("p, span, div");for(let A of C){let E=(A.textContent||"").trim().replace(/^[·•]\s*/,"");if(E==="1st"||E==="2nd"||E==="3rd"){Ge=E;break}}}let j=!!R.querySelector('svg[data-test-icon="verified"], [aria-label*="Verified" i]'),F=document.querySelector('main img[width="200"], button img[alt*="photo" i]'),P=F&&F.src||null,N=R.textContent||"",z=null,Bt=N.match(/(\d+\+?)\s+connections\b/i);Bt&&(z=Bt[1]+" connections");let Ye=null,Jt=N.match(/(\d+)\s+mutual connection/i),Vt=N.match(/and\s+(\d+)\s+other mutual/i),vo=N.match(/([A-Z][\w'-]+(?:\s+[A-Z][\w'-]+)?)\s+and\s+([A-Z][\w'-]+(?:\s+[A-Z][\w'-]+)?)\s+are\s+mutual/);Jt?Ye=parseInt(Jt[1],10):Vt?Ye=2+parseInt(Vt[1],10):vo&&(Ye=2);let Ht=null,Wt=null,Gt=_.find(C=>C.textContent&&C.textContent.trim()==="About");if(Gt){let C=Gt.parentElement;for(;C&&C.tagName!=="SECTION";)C=C.parentElement;if(C){let A=(C.textContent||"").trim();A=A.replace(/^About\s*/,"").trim();let E=A.match(/Top skills\s*(.+?)$/i);if(E){let lt=E[1].split(/\b(?:see (?:more|less)|show (?:more|less))\b/i)[0].trim().split(/\s*[·•]\s*/).map(Ao=>Ao.trim()).filter(Boolean);lt.length>0&&lt.length<10&&(Wt=lt)}A=A.split(/\n?…\s*more/i)[0].trim(),A=A.split(/\n?\.\.\.\s*more/i)[0].trim(),A=A.split(/\bTop skills\b/i)[0].trim(),A=A.split(/\b(?:see (?:more|less)|show (?:more|less))\b/i)[0].trim(),Ht=A||null}}let Yt=null,Kt=_.find(C=>C.textContent&&C.textContent.trim()==="Activity");if(Kt){let C=Kt.parentElement;for(;C&&C.tagName!=="SECTION";)C=C.parentElement;if(C){let A=(C.textContent||"").slice(0,200).match(/([\d,]+)\s+followers/i);A&&(Yt=parseInt(A[1].replace(/,/g,""),10))}}let Co=[...new Set(_.map(C=>(C.textContent||"").trim().replace(/\s*\(\d+\)$/,"").trim()).filter(C=>C&&C.length>1&&C.length<60&&C!==K).filter(C=>w.has(C)))],je=document.body.textContent||"",$o=/#OpenToWork\b/i.test(je)||/\bOpen to work\b/.test(je),xo=/#Hiring\b/i.test(je)||/\bI'?m hiring\b/i.test(je)||/\bWe are hiring\b/i.test(je);return{name:K,title:le,location:Te,currentCompany:De,currentSchool:Re,connectionDegree:Ge,hasVerifiedBadge:j,photoUrl:P,connectionsLevel:z,mutualConnectionsCount:Ye,followers:Yt,openToWork:$o,hiring:xo,about:Ht,topSkills:Wt,availableSections:Co}});console.log(`Viewed: ${d.name} - ${d.title}`);let m=null,g=null,y=null,p=null,h=null,v=null;if(o||r||i||s||c||l){let w=await Promise.resolve().then(()=>(wn(),yn));o&&(m=await w.extractExperienceFromDetails(e,t),console.log(` + Experience: ${m.length} entries`)),r&&(g=await w.extractFeaturedFromDetails(e,t),console.log(` + Featured: ${g.length} entries`)),i&&(y=await w.extractEducationFromDetails(e,t),console.log(` + Education: ${y.length} entries`)),s&&(p=await w.extractSkillsFromDetails(e,t),console.log(` + Skills: ${p.length} entries`)),c&&(h=await w.extractRecommendationsFromDetails(e,t),console.log(` + Recommendations: ${h.length} entries`)),l&&(v=await w.extractCertificationsFromDetails(e,t),console.log(` + Certifications: ${v.length} entries`))}return{...d,profileUrl:t,resolvedUrl:f,...m!==null&&{experience:m},...g!==null&&{featured:g},...y!==null&&{education:y},...p!==null&&{skillsFull:p},...h!==null&&{recommendations:h},...v!==null&&{certifications:v}}}catch(a){console.error(`Error viewing profile ${t}:`,a.message);try{await e.goto("about:blank",{timeout:1e4}),await S(1e3)}catch{}if(/session expired|bounced to|authwall|checkpoint|redirected to login/i.test(a.message||""))throw a;return null}}async function _e(e,{detail:t=!1}={}){let n=()=>e.evaluate(()=>{let r='[class*="pymk"], [class*="people-also-viewed"], [class*="browse-map"], [class*="discover"], [class*="similar"], aside',i=f=>f&&!f.closest(r),s=f=>Array.from(document.querySelectorAll(f)).filter(i),c=s('button[aria-label*="Pending"], a[aria-label*="Pending"]'),l=s('button[aria-label*="Message"], a[aria-label*="Message"]')[0]||null,a=s('button[aria-label*="Invite"][aria-label*="connect"], button[aria-label="Connect"], a[href*="custom-invite"]')[0]||null,u={pendingCount:c.length,pendingLabels:c.map(f=>f.getAttribute("aria-label")),hasMessageBtn:!!l,hasConnectBtn:!!a};return c.length>0?{status:"pending",debug:u}:l&&!a?{status:"connected",debug:u}:document.body.innerText.includes("1st degree connection")?{status:"connected",debug:u}:{status:"not_connected",debug:u}}),o=await n();return!o.debug.pendingCount&&!o.debug.hasMessageBtn&&!o.debug.hasConnectBtn&&(await S(1500),o=await n()),console.log(" DEBUG connection status:",JSON.stringify(o.debug)),t?o:o.status}async function Rt(e){let t=null,n=await e.evaluate(()=>{let a=document.querySelector("h1")?.textContent?.trim();if(a&&!a.includes("notification"))return a;let u=document.querySelector('main h2, [class*="profile"] h2, [class*="top-card"] h2');if(u&&(a=u.textContent?.trim(),a&&!a.includes("notification")))return a;let f=document.querySelector('[class*="text-heading-xlarge"]')||document.querySelector('[class*="profile-card-title"]')||document.querySelector(".pv-text-details__left-panel h1");if(f)return f.textContent?.trim()||"";let d=document.title;return d&&d.includes("|")?d.split("|")[0].trim():""});if(console.log(` Profile name: "${n}"`),n){let a=await e.evaluate(()=>{let f=document.querySelectorAll('a[href*="custom-invite"]');return{count:f.length,labels:Array.from(f).map(d=>d.getAttribute("aria-label")?.slice(0,50))}});a.count>0&&console.log(` S0: ${a.count} custom-invite links: ${JSON.stringify(a.labels)}`),t=await e.evaluateHandle(f=>{let d=document.querySelectorAll('a[href*="custom-invite"]'),m=f.toLowerCase().split(" ")[0],g='[class*="pymk"], [class*="people-also-viewed"], [class*="browse-map"], [class*="discover"], [class*="similar"], aside';for(let y of d){let p=y.getBoundingClientRect();if(p.width===0||p.height===0||y.closest(g))continue;if((y.getAttribute("aria-label")||"").toLowerCase().includes(m))return y}return null},n);let u=t?.asElement?.()||null;if(u||await t?.dispose?.().catch(()=>{}),u)return console.log(" Found: Connect link (new LinkedIn 2025)"),{button:u,method:"connect_link"}}if(n){let a=n.split(" ")[0].toLowerCase();console.log(` Looking for button with name: "${a}"`);let u=await e.evaluate(d=>{let m=new RegExp("\\b"+d.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")+"\\b","i"),g=document.querySelectorAll("button[aria-label]");return Array.from(g).filter(p=>{let h=p.getAttribute("aria-label")||"";return(h.toLowerCase().includes("invite")||h.toLowerCase().includes("connect"))&&m.test(h)}).map(p=>p.getAttribute("aria-label")?.slice(0,50))},a);u.length>0&&console.log(` S1: matched buttons: ${JSON.stringify(u)}`),t=await e.evaluateHandle(d=>{let m=document.querySelectorAll("button[aria-label]"),g='[class*="pymk"], [class*="people-also-viewed"], [class*="browse-map"], [class*="discover"], [class*="similar"], aside',y=new RegExp("\\b"+d.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")+"\\b","i");for(let p of m){if(p.closest(g))continue;let h=p.getAttribute("aria-label")||"";if(!/\b(remove|disconnect|unfollow|report|block)\b/i.test(h)&&(h.toLowerCase().includes("invite")||h.toLowerCase().includes("connect"))&&y.test(h))return p}return null},a);let f=t?.asElement?.()||null;if(f||await t?.dispose?.().catch(()=>{}),f)return console.log(" Found: button matching profile name"),{button:f,method:"name_matched_button"}}if(t=await e.$('button.artdeco-button--primary[aria-label*="Invite"]:not([class*="sticky-header"]):not([class*="muted"])'),t&&n){let a=await e.evaluate(f=>(f.getAttribute("aria-label")||"").toLowerCase(),t),u=n.split(" ")[0].toLowerCase();a.includes("invite")&&!a.includes(u)&&(console.log(` Skipping primary button: wrong person (${a})`),t=null)}if(t||(t=await e.$('button.artdeco-button--primary[aria-label="Connect"]:not([class*="sticky-header"]):not([class*="muted"])')),t)return console.log(" Found: primary button"),{button:t,method:"primary_button"};let o=await e.$$('button[aria-label*="Invite"][aria-label*="connect"]');o.length===0&&(o=await e.$$('button[aria-label="Connect"]'));let r=n?n.split(" ")[0].toLowerCase():"",i=null;for(let a of o){let u=await e.evaluate((f,d)=>{let m=f.className.includes("muted"),g=f.className.includes("sticky-header")||f.className.includes("sticky"),y=f.closest('[class*="pymk"], [class*="people-also-viewed"], [class*="browse-map"]')!==null,p=(f.getAttribute("aria-label")||"").toLowerCase(),h=p.includes("invite"),v=d?p.includes(d):!0;return{isMuted:m,isStickyHeader:g,inRecommendations:y,hasInvite:h,matchesName:v,ariaLabel:p}},a,r);if(!u.isMuted&&!u.isStickyHeader&&!u.inRecommendations){if(u.hasInvite&&!u.matchesName){console.log(` Skipping secondary button: wrong person (${u.ariaLabel})`);continue}i=a;break}}for(let a of o)a!==i&&await a.dispose().catch(()=>{});if(i)return console.log(" Found: secondary button"),{button:i,method:"secondary_button"};if(t=await e.$('section.pv-supplementary-action button[aria-label*="Invite"]'),t||(t=await e.$('section.pv-supplementary-action button[aria-label="Connect"]')),t)return console.log(" Found: supplementary section"),{button:t,method:"supplementary_section"};console.log(" Trying More actions dropdown...");let s=null,c=await e.$$('button[id*="profile-overflow-action"]');for(let a of c){let u=await e.evaluate(f=>{let d=f.getBoundingClientRect(),m=d.width>0&&d.height>0,g=f.closest('.sticky-header, [class*="sticky"]')!==null;return{isVisible:m,isInStickyHeader:g,top:d.top}},a);if(u.isVisible&&!u.isInStickyHeader){s=a,console.log(" Found More button by ID pattern (main)");break}}for(let a of c)a!==s&&await a.dispose().catch(()=>{});if(!s&&c.length>0&&(s=c[c.length-1],console.log(" Found More button by ID pattern (last)")),s||(s=await e.$('button[aria-label="More actions"]'),s&&console.log(" Found More button by aria-label")),!s){let a=await e.evaluateHandle(()=>{let f=Array.from(document.querySelectorAll("button, a")).find(m=>{let g=m.innerText?.trim(),y=m.getAttribute("aria-label")||"",p=m.getBoundingClientRect();return p.top<100||p.top>600||p.width===0?!1:g==="Follow"||g==="Message"||g==="Connect"||y.includes("Follow")||y.includes("Message")||y.includes("Invite")});if(!f)return null;let d=f.parentElement;for(let m=0;m<8&&d;m++){for(let g of d.querySelectorAll("button"))if(g.innerText?.trim()==="More"&&g!==f)return g;d=d.parentElement}return null});s=a?.asElement?.()||null,s||await a?.dispose?.().catch(()=>{}),s&&console.log(" Found More button by text (near profile actions)")}if(s)try{if(await e.evaluate(d=>d.scrollIntoView({behavior:"smooth",block:"center"}),s),await S(500),await e.evaluate(d=>d.click(),s),await S(2e3),!e.url().includes("/in/"))return console.log(" Redirect detected after More click, aborting"),null;let u=await e.$('.artdeco-dropdown__item[aria-label*="Invite"]');if(u||(u=await e.$('.artdeco-dropdown__item[aria-label*="connect" i]:not([aria-label*="remove" i]):not([aria-label*="disconnect" i])')),u||(u=await e.$('div[role="button"][aria-label*="Invite"]')),u||(u=await e.$('div[role="button"][aria-label*="connect" i]:not([aria-label*="remove" i]):not([aria-label*="disconnect" i])')),!u){let d=await e.evaluateHandle(()=>{let m=document.querySelectorAll('[role="menu"], [popover], .artdeco-dropdown__content');for(let g of m){let y=g.querySelector('a[href*="custom-invite"]');if(y)return y;for(let p of g.querySelectorAll('a, div[role="menuitem"], div[role="button"]'))if(p.innerText?.trim()==="Connect")return p}return null});u=d?.asElement?.()||null,u||await d?.dispose?.().catch(()=>{})}if(u)return console.log(" Found: More dropdown"),{button:u,method:"more_dropdown"};let f=await e.evaluate(()=>{let d=document.querySelectorAll('[role="menu"], [popover], .artdeco-dropdown__content'),m=[];for(let g of d)for(let y of g.querySelectorAll('[role="menuitem"], a, div')){let p=y.innerText?.trim();p&&p.length<40&&m.push(p)}return[...new Set(m)].slice(0,12)});if(console.log(` More dropdown items: ${JSON.stringify(f)}`),await e.keyboard.press("Escape"),f.some(d=>/remove (your )?connection/i.test(d)))return console.log(' (More menu has "Remove connection" \u2192 already connected)'),{connected:!0}}catch(a){console.log(" More dropdown click failed:",a.message),await e.keyboard.press("Escape")}let l=await e.evaluateHandle(a=>{let u='[class*="pymk"], [class*="people-also-viewed"], [class*="browse-map"], [class*="discover"], [class*="similar"], aside',f=(a||"").split(" ")[0].toLowerCase();for(let d of document.querySelectorAll("button[aria-label]")){if(d.closest(u))continue;let m=(d.getAttribute("aria-label")||"").toLowerCase();if(/\b(remove|disconnect|unfollow|report|block)\b/.test(m))continue;let g=d.getBoundingClientRect();if(!(g.width===0||g.height===0)){if(m.includes("invite")){if(f&&m.includes(f))return d;continue}if(m==="connect")return d}}return null},n||"");return t=l?.asElement?.()||null,t||await l?.dispose?.().catch(()=>{}),t?(console.log(" Found: generic button"),{button:t,method:"generic_button"}):null}async function Pr(e,t){await e.evaluate(u=>u.scrollIntoView({behavior:"smooth",block:"center"}),t),await S(500);let n=await e.evaluate(u=>({tag:u.tagName,ariaLabel:u.getAttribute("aria-label"),href:u.getAttribute("href")}),t),r=n.tag==="A"&&n.href&&n.href.includes("custom-invite"),i=n.tag==="DIV",s=(n.ariaLabel||"").toLowerCase();if(/\b(remove|disconnect|unfollow|report|block)\b/.test(s))return console.log(` \u2717 Guard: refusing a non-connect control ("${n.ariaLabel}")`),"wrong_target";if(s.includes("invite")){let u=await e.evaluate(()=>((document.querySelector("main h1")||document.querySelector("h1")||document.querySelector("main h2"))?.textContent?.trim()||"").split(" ")[0].toLowerCase());if(u&&!s.includes(u))return console.log(` \u2717 Identity guard: control is for someone else ("${n.ariaLabel}") \u2014 refusing to click (target: ${u})`),"wrong_target"}if(r){console.log(" Clicking custom-invite link (will navigate)..."),await Promise.all([e.waitForNavigation({waitUntil:"domcontentloaded",timeout:3e4}).catch(()=>{}),e.evaluate(f=>f.click(),t)]),await S(2e3);let u=e.url();if(u.includes("premium")||u.includes("redeem"))return console.log(" \u26A0 LinkedIn redirected to Premium page - connection limit reached?"),await e.goBack(),await S(2e3),"premium_redirect"}else i?(await e.evaluate(u=>u.click(),t),await S(3e3)):(await e.evaluate(u=>u.click(),t),await S(3e3));let c=e.url();if(c.includes("/premium/")||c.includes("/redeem"))return console.log(" \u26A0 LinkedIn redirected to Premium upsell page"),"premium_redirect";let l=await e.evaluate(()=>!!document.querySelector('[role="dialog"], .artdeco-modal, .send-invite')),a=e.url();return console.log(l?` \u2713 Connect sent \u2014 ${a}`:` \u26A0 No modal after connect click \u2014 ${a}`),"success"}async function Ur(e){console.log(" Upsell: Premium required for notes");let t=await e.$('button[aria-label="Dismiss"]')||await e.$("button.artdeco-modal__dismiss");t?await t.click():await e.keyboard.press("Escape"),await S(1500);let n=await Rt(e);if(n){await e.evaluate(r=>r.click(),n),await S(2e3);let o=await e.$('button[aria-label="Send without a note"]');if(o)return await Ie(e,o)?(console.log(" \u2713 Connection sent (without note)"),!0):(console.log(" \u2717 Send not confirmed (upsell path)"),!1)}return!1}async function Ie(e,t){let n='button[aria-label="Send without a note"], button[aria-label="Send invitation"], button[aria-label="Send now"]',o=()=>e.evaluate(s=>{let c=l=>!!(l&&l.querySelector(s));return!(c(document)||c(document.querySelector("#interop-outlet")?.shadowRoot))},n),r=()=>e.evaluate(()=>{for(let s of document.querySelectorAll('.artdeco-toast-item, [class*="toast"]'))if(/try again|went wrong|error|couldn.?t|please try/i.test(s.innerText||""))return!0;return!1}),i=async()=>{let s=await e.$('button[aria-label="Send without a note"]')||await e.$('button[aria-label="Send invitation"]')||await e.$('button[aria-label="Send now"]');if(s)return s;let c=await e.evaluateHandle(l=>document.querySelector("#interop-outlet")?.shadowRoot?.querySelector(l)||null,n);return s=c?.asElement?.()||null,s||await c?.dispose?.().catch(()=>{}),s};for(let s=0;s<2;s++){try{await t.evaluate(c=>c.click())}catch{}if(await S(3e3),await r())return!1;if(await o())return!0;if(s===0){let c=await i();if(!c)return await o();t=c,await S(600)}}return await o()}async function qr(e,t,n={}){let{source:o="unknown",skipQuotaGate:r=!1,skipQuotaRecord:i=!1}=n;if(!r){let c=wt();if(!c.allowed)return console.log(` \u26A0 Connection quota reached \u2014 not sending: ${c.reason}`),{success:!1,reason:"quota_exhausted",connectionSent:!1,withCustomMessage:!1,messageSent:null,method:null,error:c.reason,profileInfo:null}}let s=await Br(e,t,n);return s.connectionSent&&!i&&hn(t,o),s}async function Br(e,t,n={}){let{message:o=null,useCustomMessage:r=!0,skipIfPending:i=!0,skipIfConnected:s=!0,excludeLocations:c=[]}=n,l={success:!1,reason:null,connectionSent:!1,withCustomMessage:!1,messageSent:null,method:null,error:null,profileInfo:null,resolvedUrl:null};try{let a=t.split("/in/")[1]?.replace("/","")||"unknown";if(console.log(`
246
- --- Connecting: ${a} ---`),c.length>0){let h=re()[t]?.location;if(Vn(h,c))return console.log(` \u26A0 Excluded location (stored): ${h}`),l.reason="excluded_location",l.profileInfo={location:h},l}if(await e.goto(t,{waitUntil:"domcontentloaded",timeout:6e4}),await S(5e3),await ge(e)&&(console.log(" Dismissed Premium overlay"),await S(2e3),await Hn(e,t),await S(2e3)),process.env.LINKEDIN_DISCOVERY==="1")try{let{extractSidePanelProfiles:h,normalizeProfileUrl:v}=await Promise.resolve().then(()=>(St(),vn)),{recordDiscoveredEdges:$}=await Promise.resolve().then(()=>(Et(),_n)),{detectCategoryFromTitle:w}=await Un().then(()=>Pn),{detectSectors:x}=await Promise.resolve().then(()=>(Jn(),Bn)),T=v(t)||t,D=await h(e,{seedUrl:T}),R=D.map(k=>{let _=k.headline?w(k.headline):null;return{url:k.url,seedUrl:T,block:k.block,rank:k.rank,name:k.name,headline:k.headline,categories:_?[_]:[],sectors:x(k.headline||"")}});$(R),D.length&&console.log(` \xB7 discovery: harvested ${D.length} related profiles`)}catch(h){process.env.LINKEDIN_DISCOVERY_DEBUG==="1"&&console.log(` \xB7 discovery harvest skipped: ${h.message}`)}l.profileInfo=await e.evaluate(()=>{let h=new Set(["Highlights","About","Activity","Experience","Education","Skills","Recommendations","Languages","Interests","Volunteer experience","Featured","Honors & awards","Honors and awards","Projects","Publications","Patents","Certifications","Courses","Test scores","Organizations","More profiles for you","Explore Premium profiles","Suggested for you","Promoted","Ad Options","Don\u2019t want to see this"]),v=j=>{let F=(j||"").trim().replace(/^[·•]\s*/,"");return/^(1st|2nd|3rd|she\/her|he\/him|they\/them|their\/them)$/i.test(F)},$={name:null,title:null,location:null,about:null,company:null,connectionDegree:null,connectionsCount:null,mutualConnections:null,photoUrl:null,hasVerifiedBadge:!1},w=document.querySelector("main");if(!w)return $;let x=[...Array.from(w.querySelectorAll("h1")),...Array.from(w.querySelectorAll("h2"))],T=Array.from(w.querySelectorAll("h2")),D=x.find(j=>{let F=j.textContent&&j.textContent.trim();return F&&F.length>1&&F.length<100&&!h.has(F)&&F!=="0 notifications"}),R=D?D.textContent.trim():null,k=D?D.parentElement:null;for(;k&&k!==w&&!Array.from(k.children).filter(P=>P.tagName==="P").some(P=>{let N=P.textContent&&P.textContent.trim();return N&&N.length>5&&!v(N)});)k=k.parentElement;let _=null,X=null,K=null;if(k&&k!==w){let j=Array.from(k.children),F=j.filter(N=>N.tagName==="P").filter(N=>{let z=N.textContent&&N.textContent.trim();return z&&z.length>5&&!v(z)});F[0]&&(_=F[0].textContent.trim()),F[1]&&(X=F[1].textContent.trim());let P=j.filter(N=>N.tagName==="DIV");for(let N=P.length-1;N>=0;N--){let z=P[N].textContent&&P[N].textContent.trim();if(z&&!(R&&z.startsWith(R))&&(K=z.split(/[·•]/)[0].trim(),K))break}}let L=null;if(k){let j=k.querySelectorAll("p, span, div");for(let F of j){let P=(F.textContent||"").trim().replace(/^[·•]\s*/,"");if(P==="1st"||P==="2nd"||P==="3rd"){L=P;break}}}let le=null,De=T.find(j=>j.textContent&&j.textContent.trim()==="About");if(De){let j=De.parentElement;for(;j&&j.tagName!=="SECTION";)j=j.parentElement;j&&(le=(j.textContent||"").trim().replace(/^About\s*/,"").trim()||null,le&&(le=le.replace(/(?:…|\.\.\.)?\s*(?:see (?:more|less)|show (?:more|less))\s*$/i,"").trim()||null))}let Re=document.querySelector('main img[width="200"], button img[alt*="photo" i]'),Te=Re&&Re.src||null,Ge=!!w.querySelector('svg[data-test-icon="verified"], [aria-label*="Verified" i]');return{name:R,title:_,location:K,about:le,company:X,connectionDegree:L,connectionsCount:null,mutualConnections:null,photoUrl:Te,hasVerifiedBadge:Ge}});try{let h=e.url();h&&/\/in\/[^/?#]/.test(h)&&(l.resolvedUrl=h.split("?")[0].replace(/\/+$/,""))}catch{}if(c.length>0){let h=l.profileInfo?.location,v=re()[t]?.location,$=h||v;if(Vn($,c))return console.log(` \u26A0 Excluded location (${h?"live":"stored"}): ${$}`),l.reason="excluded_location",l;!h&&v==null&&console.log(" \u26A0 Location unknown (live scrape null, no stored value) \u2014 proceeding")}if((await _e(e,{detail:!0})).status==="pending"&&i)return console.log(" \u26A0 Already pending - skipping"),l.reason="pending",l;await ge(e);let d=await Rt(e);if(d||await ge(e)&&(console.log(" Dismissed overlay, retrying connect button search..."),await S(2e3),d=await Rt(e)),d&&d.connected)return console.log(" \u26A0 Already connected (Remove connection in More menu) - skipping"),l.reason="connected",l;if(!d){let h=await _e(e,{detail:!0});return s&&(h.status==="connected"||h.debug?.hasMessageBtn)?(console.log(" \u26A0 Already connected (no Connect control found, incl. under More) - skipping"),l.reason="connected",l):(console.log(" \u2717 Connect not found"),l.reason="button_not_found",l)}l.method=d.method;let m=await Pr(e,d.button);if(m==="wrong_target")return console.log(" \u2717 Not sent \u2014 connect control did not belong to this profile"),l.reason="wrong_target",l;if(m==="premium_redirect"){console.log(" \u26A0 Premium upsell redirect \u2014 navigating back to profile..."),await e.goto(t,{waitUntil:"domcontentloaded",timeout:6e4}),await S(4e3);let h=await _e(e);if(h==="pending")return console.log(" \u2713 Connection was sent (detected Pending after upsell redirect)"),l.success=!0,l.connectionSent=!0,l.reason="sent_without_note",l.withCustomMessage=!1,l;console.log(` Status after upsell: ${h} \u2014 trying More dropdown...`),await ge(e),await S(1e3);let v=await e.$('button[id*="profile-overflow-action"]:not([class*="sticky"])');if(v)try{await e.evaluate(w=>w.scrollIntoView({behavior:"smooth",block:"center"}),v),await S(500),await v.click().catch(()=>e.evaluate(w=>w.click(),v)),await S(2e3);let $=await e.$('.artdeco-dropdown__item[aria-label*="Invite"]')||await e.$('.artdeco-dropdown__item[aria-label*="connect" i]:not([aria-label*="remove" i]):not([aria-label*="disconnect" i])')||await e.$('div[role="button"][aria-label*="Invite"]')||await e.$('div[role="button"][aria-label*="connect" i]:not([aria-label*="remove" i]):not([aria-label*="disconnect" i])');if($){console.log(" Found Connect in More dropdown, clicking..."),await e.evaluate(T=>T.click(),$),await S(3e3);let w=e.url();if(w.includes("/premium/")||w.includes("/redeem"))return console.log(" \u2717 More dropdown also redirected to Premium"),await e.goto(t,{waitUntil:"domcontentloaded",timeout:6e4}),await S(3e3),l.reason="premium_required",l;if(await _e(e)==="pending")return console.log(" \u2713 Connection sent via More dropdown"),l.success=!0,l.connectionSent=!0,l.reason="sent_without_note",l.method="more_dropdown",l.withCustomMessage=!1,l;console.log(" More dropdown clicked, checking for modal...")}else return console.log(" No Connect option in More dropdown"),await e.keyboard.press("Escape"),await S(500),l.reason="premium_required",l}catch($){return console.log(" More dropdown failed:",$.message),await e.keyboard.press("Escape").catch(()=>{}),l.reason="premium_required",l}else return console.log(" \u2717 No More button found \u2014 Premium required"),l.reason="premium_required",l}if(await e.evaluate(()=>{let h=[document,document.querySelector("#interop-outlet")?.shadowRoot].filter(Boolean);for(let v of h){let $=v.querySelector('[role="dialog"], .artdeco-modal, .send-invite');if(!$)continue;let w=($.innerText||"").toLowerCase();if(/enter their email|verify this member knows you/.test(w)||$.querySelector('input[type="email"], input[name="email"]'))return!0;let x=$.querySelector('button[aria-label="Send without a note"]');if(x&&(x.disabled||x.getAttribute("aria-disabled")==="true"||(x.className||"").includes("disabled")))return!0}return!1})){console.log(" \u26A0 Email required to connect (LinkedIn asks for their email) - skipping");let h=await e.$('button[aria-label="Dismiss"]');if(h)try{await h.click()}catch{}else try{await e.keyboard.press("Escape")}catch{}return await S(500),l.reason="email_required",l}let y=await e.$('button[aria-label="Add a note"]'),p=await e.$('button[aria-label="Send without a note"]');if(!y&&!p){let h=await e.evaluate(()=>{let v=document.querySelector("#interop-outlet")?.shadowRoot;if(!v)return{addNote:!1,sendWithout:!1};let $=v.querySelector('button[aria-label="Add a note"]'),w=v.querySelector('button[aria-label="Send without a note"]');return{addNote:!!$,sendWithout:!!w}});if(h.addNote){let v=await e.evaluateHandle(()=>document.querySelector("#interop-outlet")?.shadowRoot?.querySelector('button[aria-label="Add a note"]'));y=v?.asElement?.()||null,y||await v.dispose().catch(()=>{})}if(h.sendWithout){let v=await e.evaluateHandle(()=>document.querySelector("#interop-outlet")?.shadowRoot?.querySelector('button[aria-label="Send without a note"]'));p=v?.asElement?.()||null,p||await v.dispose().catch(()=>{})}}if(r)if(y&&o){if(console.log(" Adding personalized note..."),await y.click(),await S(1500),await e.$("div.modal-upsell")){let $=await Ur(e);return l.success=$,l.connectionSent=$,l.reason=$?"sent_without_note_upsell":"upsell_failed",l.withCustomMessage=!1,l}let v=await e.$('textarea[name="message"]')||await e.$("textarea#custom-message")||await e.$("textarea");if(v){let $=await e.evaluate(()=>document.querySelector("h1")?.textContent?.trim()?.split(" ")[0]||""),w=o.replace("{name}",$);await v.type(w,{delay:20}),await S(500);let x=await e.$('button[aria-label="Send invitation"]')||await e.$('button[aria-label="Send now"]');if(x)return await Ie(e,x)?(console.log(" \u2713 Connection sent (with note)"),l.success=!0,l.connectionSent=!0,l.reason="sent_with_note",l.withCustomMessage=!0,l.messageSent=w,l):(console.log(" \u2717 Send not confirmed \u2014 modal stayed open / error toast; NOT counting as sent"),l.reason="modal_failed",l)}}else{if(p)return console.log(" Sending without note..."),await Ie(e,p)?(console.log(" \u2713 Connection sent (without note)"),l.success=!0,l.connectionSent=!0,l.reason="sent_without_note",l.withCustomMessage=!1,l):(console.log(" \u2717 Send not confirmed \u2014 modal stayed open / error toast; NOT counting as sent"),l.reason="modal_failed",l);if(y&&!o){console.log(" No message provided, sending without note...");let h=await e.$('button[aria-label="Send without a note"]');if(h)return await Ie(e,h)?(console.log(" \u2713 Connection sent (without note)"),l.success=!0,l.connectionSent=!0,l.reason="sent_without_note",l.withCustomMessage=!1,l):(console.log(" \u2717 Send not confirmed \u2014 modal stayed open / error toast; NOT counting as sent"),l.reason="modal_failed",l)}}else{console.log(" Sending without note (custom messages disabled)...");let h=p;if(h)return await Ie(e,h)?(console.log(" \u2713 Connection sent (without note)"),l.success=!0,l.connectionSent=!0,l.reason="sent_without_note",l.withCustomMessage=!1,l):(console.log(" \u2717 Send not confirmed \u2014 modal stayed open / error toast; NOT counting as sent"),l.reason="modal_failed",l)}if(d.method!=="more_dropdown"){console.log(" \u26A0 No modal after click \u2014 trying More dropdown fallback..."),e.url().includes("/in/")||(await e.goto(t,{waitUntil:"domcontentloaded",timeout:6e4}),await S(4e3)),await ge(e),await S(500);let v=await e.$$('button[id*="profile-overflow-action"]'),$=null;for(let w of v){let x=await e.evaluate(T=>{let D=T.getBoundingClientRect();return{visible:D.width>0&&D.height>0,inSticky:!!T.closest('.sticky-header, [class*="sticky"]')}},w);if(x.visible&&!x.inSticky){$=w;break}}!$&&v.length>0&&($=v[v.length-1]);for(let w of v)w!==$&&await w.dispose().catch(()=>{});if($)try{await e.evaluate(x=>x.scrollIntoView({behavior:"smooth",block:"center"}),$),await S(500),await $.click().catch(()=>e.evaluate(x=>x.click(),$)),await S(2e3);let w=await e.$('.artdeco-dropdown__item[aria-label*="Invite"]')||await e.$('.artdeco-dropdown__item[aria-label*="connect" i]:not([aria-label*="remove" i]):not([aria-label*="disconnect" i])')||await e.$('div[role="button"][aria-label*="Invite"]')||await e.$('div[role="button"][aria-label*="connect" i]:not([aria-label*="remove" i]):not([aria-label*="disconnect" i])');if(w){console.log(" Found Connect in More dropdown, clicking..."),l.method="more_dropdown",await e.evaluate(R=>R.click(),w),await S(3e3);let x=e.url();if(x.includes("/premium/")||x.includes("/redeem"))return console.log(" \u2717 More dropdown also redirected to Premium"),await e.goto(t,{waitUntil:"domcontentloaded",timeout:6e4}),await S(3e3),await _e(e)==="pending"?(console.log(" \u2713 Connection was sent (detected Pending)"),l.success=!0,l.connectionSent=!0,l.reason="sent_without_note",l.withCustomMessage=!1,l):(l.reason="premium_required",l);if(await _e(e)==="pending")return console.log(" \u2713 Connection sent via More dropdown"),l.success=!0,l.connectionSent=!0,l.reason="sent_without_note",l.withCustomMessage=!1,l;let D=await e.$('button[aria-label="Send without a note"]');if(D)return console.log(" Modal appeared via More dropdown, sending without note..."),await Ie(e,D)?(console.log(" \u2713 Connection sent (without note, via More dropdown)"),l.success=!0,l.connectionSent=!0,l.reason="sent_without_note",l.withCustomMessage=!1,l):(console.log(" \u2717 Send not confirmed (More-dropdown modal)"),l.reason="modal_failed",l);console.log(" More dropdown: no modal either")}else console.log(" No Connect option in More dropdown"),await e.keyboard.press("Escape")}catch(w){console.log(" More dropdown fallback failed:",w.message),await e.keyboard.press("Escape").catch(()=>{})}}return console.log(" \u2717 Could not complete connection request"),l.reason="modal_failed",l}catch(a){console.error(` \u2717 Error: ${a.message}`),l.reason="error",l.error=a.message;let u=a.message.toLowerCase();if(u.includes("detached")||u.includes("session closed")||u.includes("target closed")||u.includes("browser has disconnected")||u.includes("protocol error"))return l.fatal=!0,l;try{await e.goto("about:blank",{timeout:1e4}),await S(1e3)}catch{l.fatal=!0}return l}}async function Wn(e,t,n={}){let{message:o=null,useCustomMessage:r=!0,maxConnections:i=20,viewOnly:s=!1,delayBetweenProfiles:c=3e3,onProfileProcessed:l=null,excludeLocations:a=[],source:u="unknown"}=n,f={viewed:0,connectionsSent:0,skipped:{pending:0,connected:0,email:0,notFound:0,excludedLocation:0},errors:0};if(console.log(`
246
+ --- Connecting: ${a} ---`),c.length>0){let h=re()[t]?.location;if(Vn(h,c))return console.log(` \u26A0 Excluded location (stored): ${h}`),l.reason="excluded_location",l.profileInfo={location:h},l}if(await e.goto(t,{waitUntil:"domcontentloaded",timeout:6e4}),await S(5e3),await ge(e)&&(console.log(" Dismissed Premium overlay"),await S(2e3),await Hn(e,t),await S(2e3)),["true","1"].includes(process.env.DISCOVERY_ENABLED))try{let{extractSidePanelProfiles:h,normalizeProfileUrl:v}=await Promise.resolve().then(()=>(St(),vn)),{recordDiscoveredEdges:$}=await Promise.resolve().then(()=>(Et(),_n)),{detectCategoryFromTitle:w}=await Un().then(()=>Pn),{detectSectors:x}=await Promise.resolve().then(()=>(Jn(),Bn)),T=v(t)||t,D=await h(e,{seedUrl:T}),R=D.map(k=>{let _=k.headline?w(k.headline):null;return{url:k.url,seedUrl:T,block:k.block,rank:k.rank,name:k.name,headline:k.headline,categories:_?[_]:[],sectors:x(k.headline||"")}});$(R),D.length&&console.log(` \xB7 discovery: harvested ${D.length} related profiles`)}catch(h){["true","1"].includes(process.env.DISCOVERY_DEBUG)&&console.log(` \xB7 discovery harvest skipped: ${h.message}`)}l.profileInfo=await e.evaluate(()=>{let h=new Set(["Highlights","About","Activity","Experience","Education","Skills","Recommendations","Languages","Interests","Volunteer experience","Featured","Honors & awards","Honors and awards","Projects","Publications","Patents","Certifications","Courses","Test scores","Organizations","More profiles for you","Explore Premium profiles","Suggested for you","Promoted","Ad Options","Don\u2019t want to see this"]),v=j=>{let F=(j||"").trim().replace(/^[·•]\s*/,"");return/^(1st|2nd|3rd|she\/her|he\/him|they\/them|their\/them)$/i.test(F)},$={name:null,title:null,location:null,about:null,company:null,connectionDegree:null,connectionsCount:null,mutualConnections:null,photoUrl:null,hasVerifiedBadge:!1},w=document.querySelector("main");if(!w)return $;let x=[...Array.from(w.querySelectorAll("h1")),...Array.from(w.querySelectorAll("h2"))],T=Array.from(w.querySelectorAll("h2")),D=x.find(j=>{let F=j.textContent&&j.textContent.trim();return F&&F.length>1&&F.length<100&&!h.has(F)&&F!=="0 notifications"}),R=D?D.textContent.trim():null,k=D?D.parentElement:null;for(;k&&k!==w&&!Array.from(k.children).filter(P=>P.tagName==="P").some(P=>{let N=P.textContent&&P.textContent.trim();return N&&N.length>5&&!v(N)});)k=k.parentElement;let _=null,X=null,K=null;if(k&&k!==w){let j=Array.from(k.children),F=j.filter(N=>N.tagName==="P").filter(N=>{let z=N.textContent&&N.textContent.trim();return z&&z.length>5&&!v(z)});F[0]&&(_=F[0].textContent.trim()),F[1]&&(X=F[1].textContent.trim());let P=j.filter(N=>N.tagName==="DIV");for(let N=P.length-1;N>=0;N--){let z=P[N].textContent&&P[N].textContent.trim();if(z&&!(R&&z.startsWith(R))&&(K=z.split(/[·•]/)[0].trim(),K))break}}let L=null;if(k){let j=k.querySelectorAll("p, span, div");for(let F of j){let P=(F.textContent||"").trim().replace(/^[·•]\s*/,"");if(P==="1st"||P==="2nd"||P==="3rd"){L=P;break}}}let le=null,De=T.find(j=>j.textContent&&j.textContent.trim()==="About");if(De){let j=De.parentElement;for(;j&&j.tagName!=="SECTION";)j=j.parentElement;j&&(le=(j.textContent||"").trim().replace(/^About\s*/,"").trim()||null,le&&(le=le.replace(/(?:…|\.\.\.)?\s*(?:see (?:more|less)|show (?:more|less))\s*$/i,"").trim()||null))}let Re=document.querySelector('main img[width="200"], button img[alt*="photo" i]'),Te=Re&&Re.src||null,Ge=!!w.querySelector('svg[data-test-icon="verified"], [aria-label*="Verified" i]');return{name:R,title:_,location:K,about:le,company:X,connectionDegree:L,connectionsCount:null,mutualConnections:null,photoUrl:Te,hasVerifiedBadge:Ge}});try{let h=e.url();h&&/\/in\/[^/?#]/.test(h)&&(l.resolvedUrl=h.split("?")[0].replace(/\/+$/,""))}catch{}if(c.length>0){let h=l.profileInfo?.location,v=re()[t]?.location,$=h||v;if(Vn($,c))return console.log(` \u26A0 Excluded location (${h?"live":"stored"}): ${$}`),l.reason="excluded_location",l;!h&&v==null&&console.log(" \u26A0 Location unknown (live scrape null, no stored value) \u2014 proceeding")}if((await _e(e,{detail:!0})).status==="pending"&&i)return console.log(" \u26A0 Already pending - skipping"),l.reason="pending",l;await ge(e);let d=await Rt(e);if(d||await ge(e)&&(console.log(" Dismissed overlay, retrying connect button search..."),await S(2e3),d=await Rt(e)),d&&d.connected)return console.log(" \u26A0 Already connected (Remove connection in More menu) - skipping"),l.reason="connected",l;if(!d){let h=await _e(e,{detail:!0});return s&&(h.status==="connected"||h.debug?.hasMessageBtn)?(console.log(" \u26A0 Already connected (no Connect control found, incl. under More) - skipping"),l.reason="connected",l):(console.log(" \u2717 Connect not found"),l.reason="button_not_found",l)}l.method=d.method;let m=await Pr(e,d.button);if(m==="wrong_target")return console.log(" \u2717 Not sent \u2014 connect control did not belong to this profile"),l.reason="wrong_target",l;if(m==="premium_redirect"){console.log(" \u26A0 Premium upsell redirect \u2014 navigating back to profile..."),await e.goto(t,{waitUntil:"domcontentloaded",timeout:6e4}),await S(4e3);let h=await _e(e);if(h==="pending")return console.log(" \u2713 Connection was sent (detected Pending after upsell redirect)"),l.success=!0,l.connectionSent=!0,l.reason="sent_without_note",l.withCustomMessage=!1,l;console.log(` Status after upsell: ${h} \u2014 trying More dropdown...`),await ge(e),await S(1e3);let v=await e.$('button[id*="profile-overflow-action"]:not([class*="sticky"])');if(v)try{await e.evaluate(w=>w.scrollIntoView({behavior:"smooth",block:"center"}),v),await S(500),await v.click().catch(()=>e.evaluate(w=>w.click(),v)),await S(2e3);let $=await e.$('.artdeco-dropdown__item[aria-label*="Invite"]')||await e.$('.artdeco-dropdown__item[aria-label*="connect" i]:not([aria-label*="remove" i]):not([aria-label*="disconnect" i])')||await e.$('div[role="button"][aria-label*="Invite"]')||await e.$('div[role="button"][aria-label*="connect" i]:not([aria-label*="remove" i]):not([aria-label*="disconnect" i])');if($){console.log(" Found Connect in More dropdown, clicking..."),await e.evaluate(T=>T.click(),$),await S(3e3);let w=e.url();if(w.includes("/premium/")||w.includes("/redeem"))return console.log(" \u2717 More dropdown also redirected to Premium"),await e.goto(t,{waitUntil:"domcontentloaded",timeout:6e4}),await S(3e3),l.reason="premium_required",l;if(await _e(e)==="pending")return console.log(" \u2713 Connection sent via More dropdown"),l.success=!0,l.connectionSent=!0,l.reason="sent_without_note",l.method="more_dropdown",l.withCustomMessage=!1,l;console.log(" More dropdown clicked, checking for modal...")}else return console.log(" No Connect option in More dropdown"),await e.keyboard.press("Escape"),await S(500),l.reason="premium_required",l}catch($){return console.log(" More dropdown failed:",$.message),await e.keyboard.press("Escape").catch(()=>{}),l.reason="premium_required",l}else return console.log(" \u2717 No More button found \u2014 Premium required"),l.reason="premium_required",l}if(await e.evaluate(()=>{let h=[document,document.querySelector("#interop-outlet")?.shadowRoot].filter(Boolean);for(let v of h){let $=v.querySelector('[role="dialog"], .artdeco-modal, .send-invite');if(!$)continue;let w=($.innerText||"").toLowerCase();if(/enter their email|verify this member knows you/.test(w)||$.querySelector('input[type="email"], input[name="email"]'))return!0;let x=$.querySelector('button[aria-label="Send without a note"]');if(x&&(x.disabled||x.getAttribute("aria-disabled")==="true"||(x.className||"").includes("disabled")))return!0}return!1})){console.log(" \u26A0 Email required to connect (LinkedIn asks for their email) - skipping");let h=await e.$('button[aria-label="Dismiss"]');if(h)try{await h.click()}catch{}else try{await e.keyboard.press("Escape")}catch{}return await S(500),l.reason="email_required",l}let y=await e.$('button[aria-label="Add a note"]'),p=await e.$('button[aria-label="Send without a note"]');if(!y&&!p){let h=await e.evaluate(()=>{let v=document.querySelector("#interop-outlet")?.shadowRoot;if(!v)return{addNote:!1,sendWithout:!1};let $=v.querySelector('button[aria-label="Add a note"]'),w=v.querySelector('button[aria-label="Send without a note"]');return{addNote:!!$,sendWithout:!!w}});if(h.addNote){let v=await e.evaluateHandle(()=>document.querySelector("#interop-outlet")?.shadowRoot?.querySelector('button[aria-label="Add a note"]'));y=v?.asElement?.()||null,y||await v.dispose().catch(()=>{})}if(h.sendWithout){let v=await e.evaluateHandle(()=>document.querySelector("#interop-outlet")?.shadowRoot?.querySelector('button[aria-label="Send without a note"]'));p=v?.asElement?.()||null,p||await v.dispose().catch(()=>{})}}if(r)if(y&&o){if(console.log(" Adding personalized note..."),await y.click(),await S(1500),await e.$("div.modal-upsell")){let $=await Ur(e);return l.success=$,l.connectionSent=$,l.reason=$?"sent_without_note_upsell":"upsell_failed",l.withCustomMessage=!1,l}let v=await e.$('textarea[name="message"]')||await e.$("textarea#custom-message")||await e.$("textarea");if(v){let $=await e.evaluate(()=>document.querySelector("h1")?.textContent?.trim()?.split(" ")[0]||""),w=o.replace("{name}",$);await v.type(w,{delay:20}),await S(500);let x=await e.$('button[aria-label="Send invitation"]')||await e.$('button[aria-label="Send now"]');if(x)return await Ie(e,x)?(console.log(" \u2713 Connection sent (with note)"),l.success=!0,l.connectionSent=!0,l.reason="sent_with_note",l.withCustomMessage=!0,l.messageSent=w,l):(console.log(" \u2717 Send not confirmed \u2014 modal stayed open / error toast; NOT counting as sent"),l.reason="modal_failed",l)}}else{if(p)return console.log(" Sending without note..."),await Ie(e,p)?(console.log(" \u2713 Connection sent (without note)"),l.success=!0,l.connectionSent=!0,l.reason="sent_without_note",l.withCustomMessage=!1,l):(console.log(" \u2717 Send not confirmed \u2014 modal stayed open / error toast; NOT counting as sent"),l.reason="modal_failed",l);if(y&&!o){console.log(" No message provided, sending without note...");let h=await e.$('button[aria-label="Send without a note"]');if(h)return await Ie(e,h)?(console.log(" \u2713 Connection sent (without note)"),l.success=!0,l.connectionSent=!0,l.reason="sent_without_note",l.withCustomMessage=!1,l):(console.log(" \u2717 Send not confirmed \u2014 modal stayed open / error toast; NOT counting as sent"),l.reason="modal_failed",l)}}else{console.log(" Sending without note (custom messages disabled)...");let h=p;if(h)return await Ie(e,h)?(console.log(" \u2713 Connection sent (without note)"),l.success=!0,l.connectionSent=!0,l.reason="sent_without_note",l.withCustomMessage=!1,l):(console.log(" \u2717 Send not confirmed \u2014 modal stayed open / error toast; NOT counting as sent"),l.reason="modal_failed",l)}if(d.method!=="more_dropdown"){console.log(" \u26A0 No modal after click \u2014 trying More dropdown fallback..."),e.url().includes("/in/")||(await e.goto(t,{waitUntil:"domcontentloaded",timeout:6e4}),await S(4e3)),await ge(e),await S(500);let v=await e.$$('button[id*="profile-overflow-action"]'),$=null;for(let w of v){let x=await e.evaluate(T=>{let D=T.getBoundingClientRect();return{visible:D.width>0&&D.height>0,inSticky:!!T.closest('.sticky-header, [class*="sticky"]')}},w);if(x.visible&&!x.inSticky){$=w;break}}!$&&v.length>0&&($=v[v.length-1]);for(let w of v)w!==$&&await w.dispose().catch(()=>{});if($)try{await e.evaluate(x=>x.scrollIntoView({behavior:"smooth",block:"center"}),$),await S(500),await $.click().catch(()=>e.evaluate(x=>x.click(),$)),await S(2e3);let w=await e.$('.artdeco-dropdown__item[aria-label*="Invite"]')||await e.$('.artdeco-dropdown__item[aria-label*="connect" i]:not([aria-label*="remove" i]):not([aria-label*="disconnect" i])')||await e.$('div[role="button"][aria-label*="Invite"]')||await e.$('div[role="button"][aria-label*="connect" i]:not([aria-label*="remove" i]):not([aria-label*="disconnect" i])');if(w){console.log(" Found Connect in More dropdown, clicking..."),l.method="more_dropdown",await e.evaluate(R=>R.click(),w),await S(3e3);let x=e.url();if(x.includes("/premium/")||x.includes("/redeem"))return console.log(" \u2717 More dropdown also redirected to Premium"),await e.goto(t,{waitUntil:"domcontentloaded",timeout:6e4}),await S(3e3),await _e(e)==="pending"?(console.log(" \u2713 Connection was sent (detected Pending)"),l.success=!0,l.connectionSent=!0,l.reason="sent_without_note",l.withCustomMessage=!1,l):(l.reason="premium_required",l);if(await _e(e)==="pending")return console.log(" \u2713 Connection sent via More dropdown"),l.success=!0,l.connectionSent=!0,l.reason="sent_without_note",l.withCustomMessage=!1,l;let D=await e.$('button[aria-label="Send without a note"]');if(D)return console.log(" Modal appeared via More dropdown, sending without note..."),await Ie(e,D)?(console.log(" \u2713 Connection sent (without note, via More dropdown)"),l.success=!0,l.connectionSent=!0,l.reason="sent_without_note",l.withCustomMessage=!1,l):(console.log(" \u2717 Send not confirmed (More-dropdown modal)"),l.reason="modal_failed",l);console.log(" More dropdown: no modal either")}else console.log(" No Connect option in More dropdown"),await e.keyboard.press("Escape")}catch(w){console.log(" More dropdown fallback failed:",w.message),await e.keyboard.press("Escape").catch(()=>{})}}return console.log(" \u2717 Could not complete connection request"),l.reason="modal_failed",l}catch(a){console.error(` \u2717 Error: ${a.message}`),l.reason="error",l.error=a.message;let u=a.message.toLowerCase();if(u.includes("detached")||u.includes("session closed")||u.includes("target closed")||u.includes("browser has disconnected")||u.includes("protocol error"))return l.fatal=!0,l;try{await e.goto("about:blank",{timeout:1e4}),await S(1e3)}catch{l.fatal=!0}return l}}async function Wn(e,t,n={}){let{message:o=null,useCustomMessage:r=!0,maxConnections:i=20,viewOnly:s=!1,delayBetweenProfiles:c=3e3,onProfileProcessed:l=null,excludeLocations:a=[],source:u="unknown"}=n,f={viewed:0,connectionsSent:0,skipped:{pending:0,connected:0,email:0,notFound:0,excludedLocation:0},errors:0};if(console.log(`
247
247
  === Running Funnel: ${t.length} profiles ===`),console.log(`Mode: ${s?"View Only":`Connect (max ${i})`}`),s||console.log(`Custom messages: ${r?"Enabled":"Disabled (saving quota)"}`),a.length>0&&console.log(`Excluding locations: ${a.join(", ")}`),!s){let m=bt();console.log(`Quota: today ${m.daily.sent}/${m.daily.max} | week ${m.weekly.sent}/${m.weekly.max}`)}console.log("");let d=0;for(let m=0;m<t.length;m++){let g=t[m];try{if(e.isClosed()){console.log(`
248
248
  \u26A0 Browser page is closed \u2014 aborting funnel`);break}}catch{console.log(`
249
249
  \u26A0 Browser disconnected \u2014 aborting funnel`);break}if(!g.profileUrl){console.log(`[${m+1}/${t.length}] Skipping - no URL`);continue}let y=fn(g.profileUrl);if(y==="pending"||y==="connected"||y==="sent_with_note"||y==="sent_without_note"||y==="sent_without_note_upsell"){console.log(`[${m+1}/${t.length}] ${g.name||g.profileUrl} \u2014 skipped (db: ${y})`),y==="pending"||y==="sent_with_note"||y==="sent_without_note"||y==="sent_without_note_upsell"?f.skipped.pending++:f.skipped.connected++,l&&await l(g,f,{success:!1,reason:y,connectionSent:!1,withCustomMessage:!1,messageSent:null,method:null,error:null,profileInfo:null});continue}console.log(`[${m+1}/${t.length}] ${g.name||g.profileUrl}`);let p=null;if(s){let h=await Lr(e,g.profileUrl);h?(ze(g.profileUrl,{...h,source:u}),f.viewed++,p={success:!0,reason:"viewed",connectionSent:!1,profileInfo:h,resolvedUrl:h.resolvedUrl}):(f.errors++,p={success:!1,reason:"view_failed",connectionSent:!1,profileInfo:null})}else{if(f.connectionsSent>=i){console.log(` Max connections per session (${i}) reached`);break}let h=wt();if(!h.allowed){console.log(` \u26A0 Global quota exhausted: ${h.reason}`);break}if(p=await qr(e,g.profileUrl,{message:o,useCustomMessage:r,excludeLocations:a,source:u,skipQuotaGate:!0}),p.success)f.connectionsSent++;else switch(p.reason){case"pending":f.skipped.pending++;break;case"connected":f.skipped.connected++;break;case"email_required":f.skipped.email++;break;case"button_not_found":f.skipped.notFound++;break;case"excluded_location":f.skipped.excludedLocation++;break;default:f.errors++}}if(p?.fatal){console.log(`
package/dist/run.mjs CHANGED
@@ -243,7 +243,7 @@ function findEntryContainer(root) {
243
243
  `),console.error(" Easiest way:"),console.error(" 1. npm install -g @ilivemylife/graph-sdk"),console.error(` 2. ilml login your@email.com yourpassword
244
244
  `),console.error(` Or add ILML_TOKEN=... to .env
245
245
  `),process.exit(1));var Ie=ii({token:nn});var Be=12e4;async function Je(e,{retries:t=2}={}){let o=process.env.NODE_EASY_APPLY_MAIN;if(!o||o.startsWith("YOUR_"))throw new Error('NODE_EASY_APPLY_MAIN is not configured in .env. Create a node in iLiveMyLife, add "assist" tag, and set the node ID in .env. See README.md for setup instructions.');for(let r=1;r<=t;r++)try{let n=await Ie.askLifebot(o,e,{timeout:Be});return Be=Math.max(12e4,Be*.9),n.content}catch(n){if(n.constructor.name==="LifebotTimeoutError"&&r<t){Be=Math.min(Be*1.5,3e5),console.log(`Lifebot timeout (attempt ${r}/${t}), increasing timeout to ${Math.round(Be/1e3)}s...`);continue}throw n}}async function to(e,t){(e=="City"||e=="Location (city)")&&(e="Tell my home city (not the city where position is).");let o=`Job application form field "${e}" \u2014 reply with ONLY the value to fill in, nothing else. `;return t&&(t==="Enter a decimal number larger than 0.0"&&(t="Enter a decimal number larger than 0"),o+=`Additional info: ${t}`),await Je(o)}async function xt(e,t){let o=`Choose the best answer: ${e} Available options: ${t.join(", ")} `;return await Je(o)}async function it(e,t){let o=`Choose the best answer: ${e} Available options: ${t.join(", ")} `;return await Je(o)}ro();import{LIFEBOT_MESSAGE_TYPES as Mr}from"@ilivemylife/graph-sdk";Ue();await Qe();async function or(e,t){let o=e.url();if(o.includes("/in/"))return!1;if(o.includes("/login")||o.includes("/authwall")||o.includes("/checkpoint"))throw new Error("Session expired \u2014 redirected to login. Run: ilml linkedin login");console.log(` Off-track: ${o.slice(0,80)}`),console.log(" Navigating back to profile..."),await e.goto(t,{waitUntil:"domcontentloaded",timeout:6e4}),await w(3e3);let r=e.url();return r.includes("/in/")||console.log(` Recovery failed \u2014 still on: ${r.slice(0,80)}`),!0}function tr(e,t=[]){if(!e||!t||t.length===0)return!1;let o=e.toLowerCase();return t.some(r=>o.includes(r.toLowerCase()))}async function ls(e,t,o={}){let{extractExperience:r=!1,extractFeatured:n=!1,extractEducation:i=!1,extractSkills:s=!1,extractRecommendations:a=!1,extractCertifications:l=!1}=o;try{await e.goto(t,{waitUntil:"domcontentloaded",timeout:6e4}),await w(3e3),await oe(e)&&(console.log(" Dismissed Premium overlay"),await w(1500),await or(e,t));let c=0;for(let b=0;b<2;b++){let v=await e.evaluate(()=>document.body.scrollHeight);for(let D=1;D<=6;D++)await e.evaluate(P=>window.scrollTo(0,P),v*D/6),await w(900);let L=await e.evaluate(()=>document.body.scrollHeight);if(L===c)break;c=L}await w(800);let f=null;try{let b=e.url();b&&/\/in\/[^/?#]/.test(b)&&(f=b.split("?")[0].replace(/\/+$/,""))}catch{}let d=await e.evaluate(()=>{let b=new Set(["Highlights","About","Activity","Experience","Education","Skills","Recommendations","Languages","Interests","Volunteer experience","Featured","Honors & awards","Honors and awards","Projects","Publications","Patents","Certifications","Licenses & certifications","Courses","Test scores","Organizations"]),v=new Set(["Ad Options","Don\u2019t want to see this","0 notifications","Explore Premium profiles","You might like","People you may know","Suggested for you","More profiles for you","Promoted"]),L=$=>{let j=($||"").trim().replace(/^[·•]\s*/,"");return/^(1st|2nd|3rd|she\/her|he\/him|they\/them|their\/them)$/i.test(j)},D={name:null,title:null,location:null,currentCompany:null,currentSchool:null,connectionDegree:null,hasVerifiedBadge:!1,photoUrl:null,connectionsLevel:null,mutualConnectionsCount:null,followers:null,openToWork:!1,hiring:!1,about:null,topSkills:null,availableSections:[]},P=document.querySelector("main");if(!P)return D;let E=[...Array.from(P.querySelectorAll("h1")),...Array.from(P.querySelectorAll("h2"))],_=Array.from(P.querySelectorAll("h2")),K=E.find($=>{let j=$.textContent&&$.textContent.trim(),N=(j||"").replace(/\s*\(\d+\)$/,"").trim();return j&&j.length>1&&j.length<100&&!b.has(N)&&!v.has(N)}),Y=K?K.textContent.trim():null,W=K?K.parentElement:null;for(;W&&W!==P&&!Array.from(W.children).filter(j=>j.tagName==="P").some(j=>{let N=j.textContent&&j.textContent.trim();return N&&N.length>5&&!L(N)});)W=W.parentElement;let V=null,T=null,B=null,R=null;if(W&&W!==P){let $=Array.from(W.children).filter(N=>N.tagName==="P").filter(N=>{let Se=N.textContent&&N.textContent.trim();return Se&&Se.length>5&&!L(Se)});if($[0]&&(V=$[0].textContent.trim()),$[1]){let N=$[1].textContent.trim().split(/\s*[·•]\s*/);T=N[0]||null,N.length>1&&(B=N.slice(1).join(" \xB7 "))}let j=Array.from(W.children).filter(N=>N.tagName==="DIV");for(let N=j.length-1;N>=0;N--){let Se=j[N].textContent&&j[N].textContent.trim();if(Se&&!(Y&&Se.startsWith(Y))&&(R=Se.split(/[·•]/)[0].trim(),R))break}}let O=null;if(W){let $=W.querySelectorAll("p, span, div");for(let j of $){let N=(j.textContent||"").trim().replace(/^[·•]\s*/,"");if(N==="1st"||N==="2nd"||N==="3rd"){O=N;break}}}let C=!!P.querySelector('svg[data-test-icon="verified"], [aria-label*="Verified" i]'),M=document.querySelector('main img[width="200"], button img[alt*="photo" i]'),q=M&&M.src||null,I=P.textContent||"",G=null,ue=I.match(/(\d+\+?)\s+connections\b/i);ue&&(G=ue[1]+" connections");let de=null,Zt=I.match(/(\d+)\s+mutual connection/i),zo=I.match(/and\s+(\d+)\s+other mutual/i),Zr=I.match(/([A-Z][\w'-]+(?:\s+[A-Z][\w'-]+)?)\s+and\s+([A-Z][\w'-]+(?:\s+[A-Z][\w'-]+)?)\s+are\s+mutual/);Zt?de=parseInt(Zt[1],10):zo?de=2+parseInt(zo[1],10):Zr&&(de=2);let Qo=null,Xo=null,Zo=_.find($=>$.textContent&&$.textContent.trim()==="About");if(Zo){let $=Zo.parentElement;for(;$&&$.tagName!=="SECTION";)$=$.parentElement;if($){let j=($.textContent||"").trim();j=j.replace(/^About\s*/,"").trim();let N=j.match(/Top skills\s*(.+?)$/i);if(N){let eo=N[1].split(/\b(?:see (?:more|less)|show (?:more|less))\b/i)[0].trim().split(/\s*[·•]\s*/).map(ni=>ni.trim()).filter(Boolean);eo.length>0&&eo.length<10&&(Xo=eo)}j=j.split(/\n?…\s*more/i)[0].trim(),j=j.split(/\n?\.\.\.\s*more/i)[0].trim(),j=j.split(/\bTop skills\b/i)[0].trim(),j=j.split(/\b(?:see (?:more|less)|show (?:more|less))\b/i)[0].trim(),Qo=j||null}}let en=null,tn=_.find($=>$.textContent&&$.textContent.trim()==="Activity");if(tn){let $=tn.parentElement;for(;$&&$.tagName!=="SECTION";)$=$.parentElement;if($){let j=($.textContent||"").slice(0,200).match(/([\d,]+)\s+followers/i);j&&(en=parseInt(j[1].replace(/,/g,""),10))}}let ei=[...new Set(_.map($=>($.textContent||"").trim().replace(/\s*\(\d+\)$/,"").trim()).filter($=>$&&$.length>1&&$.length<60&&$!==Y).filter($=>b.has($)))],rt=document.body.textContent||"",ti=/#OpenToWork\b/i.test(rt)||/\bOpen to work\b/.test(rt),oi=/#Hiring\b/i.test(rt)||/\bI'?m hiring\b/i.test(rt)||/\bWe are hiring\b/i.test(rt);return{name:Y,title:V,location:R,currentCompany:T,currentSchool:B,connectionDegree:O,hasVerifiedBadge:C,photoUrl:q,connectionsLevel:G,mutualConnectionsCount:de,followers:en,openToWork:ti,hiring:oi,about:Qo,topSkills:Xo,availableSections:ei}});console.log(`Viewed: ${d.name} - ${d.title}`);let p=null,m=null,h=null,g=null,y=null,S=null;if(r||n||i||s||a||l){let b=await Promise.resolve().then(()=>(Tn(),_n));r&&(p=await b.extractExperienceFromDetails(e,t),console.log(` + Experience: ${p.length} entries`)),n&&(m=await b.extractFeaturedFromDetails(e,t),console.log(` + Featured: ${m.length} entries`)),i&&(h=await b.extractEducationFromDetails(e,t),console.log(` + Education: ${h.length} entries`)),s&&(g=await b.extractSkillsFromDetails(e,t),console.log(` + Skills: ${g.length} entries`)),a&&(y=await b.extractRecommendationsFromDetails(e,t),console.log(` + Recommendations: ${y.length} entries`)),l&&(S=await b.extractCertificationsFromDetails(e,t),console.log(` + Certifications: ${S.length} entries`))}return{...d,profileUrl:t,resolvedUrl:f,...p!==null&&{experience:p},...m!==null&&{featured:m},...h!==null&&{education:h},...g!==null&&{skillsFull:g},...y!==null&&{recommendations:y},...S!==null&&{certifications:S}}}catch(u){console.error(`Error viewing profile ${t}:`,u.message);try{await e.goto("about:blank",{timeout:1e4}),await w(1e3)}catch{}if(/session expired|bounced to|authwall|checkpoint|redirected to login/i.test(u.message||""))throw u;return null}}async function tt(e,{detail:t=!1}={}){let o=()=>e.evaluate(()=>{let n='[class*="pymk"], [class*="people-also-viewed"], [class*="browse-map"], [class*="discover"], [class*="similar"], aside',i=f=>f&&!f.closest(n),s=f=>Array.from(document.querySelectorAll(f)).filter(i),a=s('button[aria-label*="Pending"], a[aria-label*="Pending"]'),l=s('button[aria-label*="Message"], a[aria-label*="Message"]')[0]||null,u=s('button[aria-label*="Invite"][aria-label*="connect"], button[aria-label="Connect"], a[href*="custom-invite"]')[0]||null,c={pendingCount:a.length,pendingLabels:a.map(f=>f.getAttribute("aria-label")),hasMessageBtn:!!l,hasConnectBtn:!!u};return a.length>0?{status:"pending",debug:c}:l&&!u?{status:"connected",debug:c}:document.body.innerText.includes("1st degree connection")?{status:"connected",debug:c}:{status:"not_connected",debug:c}}),r=await o();return!r.debug.pendingCount&&!r.debug.hasMessageBtn&&!r.debug.hasConnectBtn&&(await w(1500),r=await o()),console.log(" DEBUG connection status:",JSON.stringify(r.debug)),t?r:r.status}async function To(e){let t=null,o=await e.evaluate(()=>{let u=document.querySelector("h1")?.textContent?.trim();if(u&&!u.includes("notification"))return u;let c=document.querySelector('main h2, [class*="profile"] h2, [class*="top-card"] h2');if(c&&(u=c.textContent?.trim(),u&&!u.includes("notification")))return u;let f=document.querySelector('[class*="text-heading-xlarge"]')||document.querySelector('[class*="profile-card-title"]')||document.querySelector(".pv-text-details__left-panel h1");if(f)return f.textContent?.trim()||"";let d=document.title;return d&&d.includes("|")?d.split("|")[0].trim():""});if(console.log(` Profile name: "${o}"`),o){let u=await e.evaluate(()=>{let f=document.querySelectorAll('a[href*="custom-invite"]');return{count:f.length,labels:Array.from(f).map(d=>d.getAttribute("aria-label")?.slice(0,50))}});u.count>0&&console.log(` S0: ${u.count} custom-invite links: ${JSON.stringify(u.labels)}`),t=await e.evaluateHandle(f=>{let d=document.querySelectorAll('a[href*="custom-invite"]'),p=f.toLowerCase().split(" ")[0],m='[class*="pymk"], [class*="people-also-viewed"], [class*="browse-map"], [class*="discover"], [class*="similar"], aside';for(let h of d){let g=h.getBoundingClientRect();if(g.width===0||g.height===0||h.closest(m))continue;if((h.getAttribute("aria-label")||"").toLowerCase().includes(p))return h}return null},o);let c=t?.asElement?.()||null;if(c||await t?.dispose?.().catch(()=>{}),c)return console.log(" Found: Connect link (new LinkedIn 2025)"),{button:c,method:"connect_link"}}if(o){let u=o.split(" ")[0].toLowerCase();console.log(` Looking for button with name: "${u}"`);let c=await e.evaluate(d=>{let p=new RegExp("\\b"+d.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")+"\\b","i"),m=document.querySelectorAll("button[aria-label]");return Array.from(m).filter(g=>{let y=g.getAttribute("aria-label")||"";return(y.toLowerCase().includes("invite")||y.toLowerCase().includes("connect"))&&p.test(y)}).map(g=>g.getAttribute("aria-label")?.slice(0,50))},u);c.length>0&&console.log(` S1: matched buttons: ${JSON.stringify(c)}`),t=await e.evaluateHandle(d=>{let p=document.querySelectorAll("button[aria-label]"),m='[class*="pymk"], [class*="people-also-viewed"], [class*="browse-map"], [class*="discover"], [class*="similar"], aside',h=new RegExp("\\b"+d.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")+"\\b","i");for(let g of p){if(g.closest(m))continue;let y=g.getAttribute("aria-label")||"";if(!/\b(remove|disconnect|unfollow|report|block)\b/i.test(y)&&(y.toLowerCase().includes("invite")||y.toLowerCase().includes("connect"))&&h.test(y))return g}return null},u);let f=t?.asElement?.()||null;if(f||await t?.dispose?.().catch(()=>{}),f)return console.log(" Found: button matching profile name"),{button:f,method:"name_matched_button"}}if(t=await e.$('button.artdeco-button--primary[aria-label*="Invite"]:not([class*="sticky-header"]):not([class*="muted"])'),t&&o){let u=await e.evaluate(f=>(f.getAttribute("aria-label")||"").toLowerCase(),t),c=o.split(" ")[0].toLowerCase();u.includes("invite")&&!u.includes(c)&&(console.log(` Skipping primary button: wrong person (${u})`),t=null)}if(t||(t=await e.$('button.artdeco-button--primary[aria-label="Connect"]:not([class*="sticky-header"]):not([class*="muted"])')),t)return console.log(" Found: primary button"),{button:t,method:"primary_button"};let r=await e.$$('button[aria-label*="Invite"][aria-label*="connect"]');r.length===0&&(r=await e.$$('button[aria-label="Connect"]'));let n=o?o.split(" ")[0].toLowerCase():"",i=null;for(let u of r){let c=await e.evaluate((f,d)=>{let p=f.className.includes("muted"),m=f.className.includes("sticky-header")||f.className.includes("sticky"),h=f.closest('[class*="pymk"], [class*="people-also-viewed"], [class*="browse-map"]')!==null,g=(f.getAttribute("aria-label")||"").toLowerCase(),y=g.includes("invite"),S=d?g.includes(d):!0;return{isMuted:p,isStickyHeader:m,inRecommendations:h,hasInvite:y,matchesName:S,ariaLabel:g}},u,n);if(!c.isMuted&&!c.isStickyHeader&&!c.inRecommendations){if(c.hasInvite&&!c.matchesName){console.log(` Skipping secondary button: wrong person (${c.ariaLabel})`);continue}i=u;break}}for(let u of r)u!==i&&await u.dispose().catch(()=>{});if(i)return console.log(" Found: secondary button"),{button:i,method:"secondary_button"};if(t=await e.$('section.pv-supplementary-action button[aria-label*="Invite"]'),t||(t=await e.$('section.pv-supplementary-action button[aria-label="Connect"]')),t)return console.log(" Found: supplementary section"),{button:t,method:"supplementary_section"};console.log(" Trying More actions dropdown...");let s=null,a=await e.$$('button[id*="profile-overflow-action"]');for(let u of a){let c=await e.evaluate(f=>{let d=f.getBoundingClientRect(),p=d.width>0&&d.height>0,m=f.closest('.sticky-header, [class*="sticky"]')!==null;return{isVisible:p,isInStickyHeader:m,top:d.top}},u);if(c.isVisible&&!c.isInStickyHeader){s=u,console.log(" Found More button by ID pattern (main)");break}}for(let u of a)u!==s&&await u.dispose().catch(()=>{});if(!s&&a.length>0&&(s=a[a.length-1],console.log(" Found More button by ID pattern (last)")),s||(s=await e.$('button[aria-label="More actions"]'),s&&console.log(" Found More button by aria-label")),!s){let u=await e.evaluateHandle(()=>{let f=Array.from(document.querySelectorAll("button, a")).find(p=>{let m=p.innerText?.trim(),h=p.getAttribute("aria-label")||"",g=p.getBoundingClientRect();return g.top<100||g.top>600||g.width===0?!1:m==="Follow"||m==="Message"||m==="Connect"||h.includes("Follow")||h.includes("Message")||h.includes("Invite")});if(!f)return null;let d=f.parentElement;for(let p=0;p<8&&d;p++){for(let m of d.querySelectorAll("button"))if(m.innerText?.trim()==="More"&&m!==f)return m;d=d.parentElement}return null});s=u?.asElement?.()||null,s||await u?.dispose?.().catch(()=>{}),s&&console.log(" Found More button by text (near profile actions)")}if(s)try{if(await e.evaluate(d=>d.scrollIntoView({behavior:"smooth",block:"center"}),s),await w(500),await e.evaluate(d=>d.click(),s),await w(2e3),!e.url().includes("/in/"))return console.log(" Redirect detected after More click, aborting"),null;let c=await e.$('.artdeco-dropdown__item[aria-label*="Invite"]');if(c||(c=await e.$('.artdeco-dropdown__item[aria-label*="connect" i]:not([aria-label*="remove" i]):not([aria-label*="disconnect" i])')),c||(c=await e.$('div[role="button"][aria-label*="Invite"]')),c||(c=await e.$('div[role="button"][aria-label*="connect" i]:not([aria-label*="remove" i]):not([aria-label*="disconnect" i])')),!c){let d=await e.evaluateHandle(()=>{let p=document.querySelectorAll('[role="menu"], [popover], .artdeco-dropdown__content');for(let m of p){let h=m.querySelector('a[href*="custom-invite"]');if(h)return h;for(let g of m.querySelectorAll('a, div[role="menuitem"], div[role="button"]'))if(g.innerText?.trim()==="Connect")return g}return null});c=d?.asElement?.()||null,c||await d?.dispose?.().catch(()=>{})}if(c)return console.log(" Found: More dropdown"),{button:c,method:"more_dropdown"};let f=await e.evaluate(()=>{let d=document.querySelectorAll('[role="menu"], [popover], .artdeco-dropdown__content'),p=[];for(let m of d)for(let h of m.querySelectorAll('[role="menuitem"], a, div')){let g=h.innerText?.trim();g&&g.length<40&&p.push(g)}return[...new Set(p)].slice(0,12)});if(console.log(` More dropdown items: ${JSON.stringify(f)}`),await e.keyboard.press("Escape"),f.some(d=>/remove (your )?connection/i.test(d)))return console.log(' (More menu has "Remove connection" \u2192 already connected)'),{connected:!0}}catch(u){console.log(" More dropdown click failed:",u.message),await e.keyboard.press("Escape")}let l=await e.evaluateHandle(u=>{let c='[class*="pymk"], [class*="people-also-viewed"], [class*="browse-map"], [class*="discover"], [class*="similar"], aside',f=(u||"").split(" ")[0].toLowerCase();for(let d of document.querySelectorAll("button[aria-label]")){if(d.closest(c))continue;let p=(d.getAttribute("aria-label")||"").toLowerCase();if(/\b(remove|disconnect|unfollow|report|block)\b/.test(p))continue;let m=d.getBoundingClientRect();if(!(m.width===0||m.height===0)){if(p.includes("invite")){if(f&&p.includes(f))return d;continue}if(p==="connect")return d}}return null},o||"");return t=l?.asElement?.()||null,t||await l?.dispose?.().catch(()=>{}),t?(console.log(" Found: generic button"),{button:t,method:"generic_button"}):null}async function as(e,t){await e.evaluate(c=>c.scrollIntoView({behavior:"smooth",block:"center"}),t),await w(500);let o=await e.evaluate(c=>({tag:c.tagName,ariaLabel:c.getAttribute("aria-label"),href:c.getAttribute("href")}),t),n=o.tag==="A"&&o.href&&o.href.includes("custom-invite"),i=o.tag==="DIV",s=(o.ariaLabel||"").toLowerCase();if(/\b(remove|disconnect|unfollow|report|block)\b/.test(s))return console.log(` \u2717 Guard: refusing a non-connect control ("${o.ariaLabel}")`),"wrong_target";if(s.includes("invite")){let c=await e.evaluate(()=>((document.querySelector("main h1")||document.querySelector("h1")||document.querySelector("main h2"))?.textContent?.trim()||"").split(" ")[0].toLowerCase());if(c&&!s.includes(c))return console.log(` \u2717 Identity guard: control is for someone else ("${o.ariaLabel}") \u2014 refusing to click (target: ${c})`),"wrong_target"}if(n){console.log(" Clicking custom-invite link (will navigate)..."),await Promise.all([e.waitForNavigation({waitUntil:"domcontentloaded",timeout:3e4}).catch(()=>{}),e.evaluate(f=>f.click(),t)]),await w(2e3);let c=e.url();if(c.includes("premium")||c.includes("redeem"))return console.log(" \u26A0 LinkedIn redirected to Premium page - connection limit reached?"),await e.goBack(),await w(2e3),"premium_redirect"}else i?(await e.evaluate(c=>c.click(),t),await w(3e3)):(await e.evaluate(c=>c.click(),t),await w(3e3));let a=e.url();if(a.includes("/premium/")||a.includes("/redeem"))return console.log(" \u26A0 LinkedIn redirected to Premium upsell page"),"premium_redirect";let l=await e.evaluate(()=>!!document.querySelector('[role="dialog"], .artdeco-modal, .send-invite')),u=e.url();return console.log(l?` \u2713 Connect sent \u2014 ${u}`:` \u26A0 No modal after connect click \u2014 ${u}`),"success"}async function cs(e){console.log(" Upsell: Premium required for notes");let t=await e.$('button[aria-label="Dismiss"]')||await e.$("button.artdeco-modal__dismiss");t?await t.click():await e.keyboard.press("Escape"),await w(1500);let o=await To(e);if(o){await e.evaluate(n=>n.click(),o),await w(2e3);let r=await e.$('button[aria-label="Send without a note"]');if(r)return await ot(e,r)?(console.log(" \u2713 Connection sent (without note)"),!0):(console.log(" \u2717 Send not confirmed (upsell path)"),!1)}return!1}async function ot(e,t){let o='button[aria-label="Send without a note"], button[aria-label="Send invitation"], button[aria-label="Send now"]',r=()=>e.evaluate(s=>{let a=l=>!!(l&&l.querySelector(s));return!(a(document)||a(document.querySelector("#interop-outlet")?.shadowRoot))},o),n=()=>e.evaluate(()=>{for(let s of document.querySelectorAll('.artdeco-toast-item, [class*="toast"]'))if(/try again|went wrong|error|couldn.?t|please try/i.test(s.innerText||""))return!0;return!1}),i=async()=>{let s=await e.$('button[aria-label="Send without a note"]')||await e.$('button[aria-label="Send invitation"]')||await e.$('button[aria-label="Send now"]');if(s)return s;let a=await e.evaluateHandle(l=>document.querySelector("#interop-outlet")?.shadowRoot?.querySelector(l)||null,o);return s=a?.asElement?.()||null,s||await a?.dispose?.().catch(()=>{}),s};for(let s=0;s<2;s++){try{await t.evaluate(a=>a.click())}catch{}if(await w(3e3),await n())return!1;if(await r())return!0;if(s===0){let a=await i();if(!a)return await r();t=a,await w(600)}}return await r()}async function us(e,t,o={}){let{source:r="unknown",skipQuotaGate:n=!1,skipQuotaRecord:i=!1}=o;if(!n){let a=ut();if(!a.allowed)return console.log(` \u26A0 Connection quota reached \u2014 not sending: ${a.reason}`),{success:!1,reason:"quota_exhausted",connectionSent:!1,withCustomMessage:!1,messageSent:null,method:null,error:a.reason,profileInfo:null}}let s=await ds(e,t,o);return s.connectionSent&&!i&&Pt(t,r),s}async function ds(e,t,o={}){let{message:r=null,useCustomMessage:n=!0,skipIfPending:i=!0,skipIfConnected:s=!0,excludeLocations:a=[]}=o,l={success:!1,reason:null,connectionSent:!1,withCustomMessage:!1,messageSent:null,method:null,error:null,profileInfo:null,resolvedUrl:null};try{let u=t.split("/in/")[1]?.replace("/","")||"unknown";if(console.log(`
246
- --- Connecting: ${u} ---`),a.length>0){let y=Q()[t]?.location;if(tr(y,a))return console.log(` \u26A0 Excluded location (stored): ${y}`),l.reason="excluded_location",l.profileInfo={location:y},l}if(await e.goto(t,{waitUntil:"domcontentloaded",timeout:6e4}),await w(5e3),await oe(e)&&(console.log(" Dismissed Premium overlay"),await w(2e3),await or(e,t),await w(2e3)),process.env.LINKEDIN_DISCOVERY==="1")try{let{extractSidePanelProfiles:y,normalizeProfileUrl:S}=await Promise.resolve().then(()=>(go(),jn)),{recordDiscoveredEdges:x}=await Promise.resolve().then(()=>(Jt(),qn)),{detectCategoryFromTitle:b}=await Qn().then(()=>zn),{detectSectors:v}=await Promise.resolve().then(()=>(er(),Zn)),L=S(t)||t,D=await y(e,{seedUrl:L}),P=D.map(E=>{let _=E.headline?b(E.headline):null;return{url:E.url,seedUrl:L,block:E.block,rank:E.rank,name:E.name,headline:E.headline,categories:_?[_]:[],sectors:v(E.headline||"")}});x(P),D.length&&console.log(` \xB7 discovery: harvested ${D.length} related profiles`)}catch(y){process.env.LINKEDIN_DISCOVERY_DEBUG==="1"&&console.log(` \xB7 discovery harvest skipped: ${y.message}`)}l.profileInfo=await e.evaluate(()=>{let y=new Set(["Highlights","About","Activity","Experience","Education","Skills","Recommendations","Languages","Interests","Volunteer experience","Featured","Honors & awards","Honors and awards","Projects","Publications","Patents","Certifications","Courses","Test scores","Organizations","More profiles for you","Explore Premium profiles","Suggested for you","Promoted","Ad Options","Don\u2019t want to see this"]),S=C=>{let M=(C||"").trim().replace(/^[·•]\s*/,"");return/^(1st|2nd|3rd|she\/her|he\/him|they\/them|their\/them)$/i.test(M)},x={name:null,title:null,location:null,about:null,company:null,connectionDegree:null,connectionsCount:null,mutualConnections:null,photoUrl:null,hasVerifiedBadge:!1},b=document.querySelector("main");if(!b)return x;let v=[...Array.from(b.querySelectorAll("h1")),...Array.from(b.querySelectorAll("h2"))],L=Array.from(b.querySelectorAll("h2")),D=v.find(C=>{let M=C.textContent&&C.textContent.trim();return M&&M.length>1&&M.length<100&&!y.has(M)&&M!=="0 notifications"}),P=D?D.textContent.trim():null,E=D?D.parentElement:null;for(;E&&E!==b&&!Array.from(E.children).filter(q=>q.tagName==="P").some(q=>{let I=q.textContent&&q.textContent.trim();return I&&I.length>5&&!S(I)});)E=E.parentElement;let _=null,K=null,Y=null;if(E&&E!==b){let C=Array.from(E.children),M=C.filter(I=>I.tagName==="P").filter(I=>{let G=I.textContent&&I.textContent.trim();return G&&G.length>5&&!S(G)});M[0]&&(_=M[0].textContent.trim()),M[1]&&(K=M[1].textContent.trim());let q=C.filter(I=>I.tagName==="DIV");for(let I=q.length-1;I>=0;I--){let G=q[I].textContent&&q[I].textContent.trim();if(G&&!(P&&G.startsWith(P))&&(Y=G.split(/[·•]/)[0].trim(),Y))break}}let W=null;if(E){let C=E.querySelectorAll("p, span, div");for(let M of C){let q=(M.textContent||"").trim().replace(/^[·•]\s*/,"");if(q==="1st"||q==="2nd"||q==="3rd"){W=q;break}}}let V=null,T=L.find(C=>C.textContent&&C.textContent.trim()==="About");if(T){let C=T.parentElement;for(;C&&C.tagName!=="SECTION";)C=C.parentElement;C&&(V=(C.textContent||"").trim().replace(/^About\s*/,"").trim()||null,V&&(V=V.replace(/(?:…|\.\.\.)?\s*(?:see (?:more|less)|show (?:more|less))\s*$/i,"").trim()||null))}let B=document.querySelector('main img[width="200"], button img[alt*="photo" i]'),R=B&&B.src||null,O=!!b.querySelector('svg[data-test-icon="verified"], [aria-label*="Verified" i]');return{name:P,title:_,location:Y,about:V,company:K,connectionDegree:W,connectionsCount:null,mutualConnections:null,photoUrl:R,hasVerifiedBadge:O}});try{let y=e.url();y&&/\/in\/[^/?#]/.test(y)&&(l.resolvedUrl=y.split("?")[0].replace(/\/+$/,""))}catch{}if(a.length>0){let y=l.profileInfo?.location,S=Q()[t]?.location,x=y||S;if(tr(x,a))return console.log(` \u26A0 Excluded location (${y?"live":"stored"}): ${x}`),l.reason="excluded_location",l;!y&&S==null&&console.log(" \u26A0 Location unknown (live scrape null, no stored value) \u2014 proceeding")}if((await tt(e,{detail:!0})).status==="pending"&&i)return console.log(" \u26A0 Already pending - skipping"),l.reason="pending",l;await oe(e);let d=await To(e);if(d||await oe(e)&&(console.log(" Dismissed overlay, retrying connect button search..."),await w(2e3),d=await To(e)),d&&d.connected)return console.log(" \u26A0 Already connected (Remove connection in More menu) - skipping"),l.reason="connected",l;if(!d){let y=await tt(e,{detail:!0});return s&&(y.status==="connected"||y.debug?.hasMessageBtn)?(console.log(" \u26A0 Already connected (no Connect control found, incl. under More) - skipping"),l.reason="connected",l):(console.log(" \u2717 Connect not found"),l.reason="button_not_found",l)}l.method=d.method;let p=await as(e,d.button);if(p==="wrong_target")return console.log(" \u2717 Not sent \u2014 connect control did not belong to this profile"),l.reason="wrong_target",l;if(p==="premium_redirect"){console.log(" \u26A0 Premium upsell redirect \u2014 navigating back to profile..."),await e.goto(t,{waitUntil:"domcontentloaded",timeout:6e4}),await w(4e3);let y=await tt(e);if(y==="pending")return console.log(" \u2713 Connection was sent (detected Pending after upsell redirect)"),l.success=!0,l.connectionSent=!0,l.reason="sent_without_note",l.withCustomMessage=!1,l;console.log(` Status after upsell: ${y} \u2014 trying More dropdown...`),await oe(e),await w(1e3);let S=await e.$('button[id*="profile-overflow-action"]:not([class*="sticky"])');if(S)try{await e.evaluate(b=>b.scrollIntoView({behavior:"smooth",block:"center"}),S),await w(500),await S.click().catch(()=>e.evaluate(b=>b.click(),S)),await w(2e3);let x=await e.$('.artdeco-dropdown__item[aria-label*="Invite"]')||await e.$('.artdeco-dropdown__item[aria-label*="connect" i]:not([aria-label*="remove" i]):not([aria-label*="disconnect" i])')||await e.$('div[role="button"][aria-label*="Invite"]')||await e.$('div[role="button"][aria-label*="connect" i]:not([aria-label*="remove" i]):not([aria-label*="disconnect" i])');if(x){console.log(" Found Connect in More dropdown, clicking..."),await e.evaluate(L=>L.click(),x),await w(3e3);let b=e.url();if(b.includes("/premium/")||b.includes("/redeem"))return console.log(" \u2717 More dropdown also redirected to Premium"),await e.goto(t,{waitUntil:"domcontentloaded",timeout:6e4}),await w(3e3),l.reason="premium_required",l;if(await tt(e)==="pending")return console.log(" \u2713 Connection sent via More dropdown"),l.success=!0,l.connectionSent=!0,l.reason="sent_without_note",l.method="more_dropdown",l.withCustomMessage=!1,l;console.log(" More dropdown clicked, checking for modal...")}else return console.log(" No Connect option in More dropdown"),await e.keyboard.press("Escape"),await w(500),l.reason="premium_required",l}catch(x){return console.log(" More dropdown failed:",x.message),await e.keyboard.press("Escape").catch(()=>{}),l.reason="premium_required",l}else return console.log(" \u2717 No More button found \u2014 Premium required"),l.reason="premium_required",l}if(await e.evaluate(()=>{let y=[document,document.querySelector("#interop-outlet")?.shadowRoot].filter(Boolean);for(let S of y){let x=S.querySelector('[role="dialog"], .artdeco-modal, .send-invite');if(!x)continue;let b=(x.innerText||"").toLowerCase();if(/enter their email|verify this member knows you/.test(b)||x.querySelector('input[type="email"], input[name="email"]'))return!0;let v=x.querySelector('button[aria-label="Send without a note"]');if(v&&(v.disabled||v.getAttribute("aria-disabled")==="true"||(v.className||"").includes("disabled")))return!0}return!1})){console.log(" \u26A0 Email required to connect (LinkedIn asks for their email) - skipping");let y=await e.$('button[aria-label="Dismiss"]');if(y)try{await y.click()}catch{}else try{await e.keyboard.press("Escape")}catch{}return await w(500),l.reason="email_required",l}let h=await e.$('button[aria-label="Add a note"]'),g=await e.$('button[aria-label="Send without a note"]');if(!h&&!g){let y=await e.evaluate(()=>{let S=document.querySelector("#interop-outlet")?.shadowRoot;if(!S)return{addNote:!1,sendWithout:!1};let x=S.querySelector('button[aria-label="Add a note"]'),b=S.querySelector('button[aria-label="Send without a note"]');return{addNote:!!x,sendWithout:!!b}});if(y.addNote){let S=await e.evaluateHandle(()=>document.querySelector("#interop-outlet")?.shadowRoot?.querySelector('button[aria-label="Add a note"]'));h=S?.asElement?.()||null,h||await S.dispose().catch(()=>{})}if(y.sendWithout){let S=await e.evaluateHandle(()=>document.querySelector("#interop-outlet")?.shadowRoot?.querySelector('button[aria-label="Send without a note"]'));g=S?.asElement?.()||null,g||await S.dispose().catch(()=>{})}}if(n)if(h&&r){if(console.log(" Adding personalized note..."),await h.click(),await w(1500),await e.$("div.modal-upsell")){let x=await cs(e);return l.success=x,l.connectionSent=x,l.reason=x?"sent_without_note_upsell":"upsell_failed",l.withCustomMessage=!1,l}let S=await e.$('textarea[name="message"]')||await e.$("textarea#custom-message")||await e.$("textarea");if(S){let x=await e.evaluate(()=>document.querySelector("h1")?.textContent?.trim()?.split(" ")[0]||""),b=r.replace("{name}",x);await S.type(b,{delay:20}),await w(500);let v=await e.$('button[aria-label="Send invitation"]')||await e.$('button[aria-label="Send now"]');if(v)return await ot(e,v)?(console.log(" \u2713 Connection sent (with note)"),l.success=!0,l.connectionSent=!0,l.reason="sent_with_note",l.withCustomMessage=!0,l.messageSent=b,l):(console.log(" \u2717 Send not confirmed \u2014 modal stayed open / error toast; NOT counting as sent"),l.reason="modal_failed",l)}}else{if(g)return console.log(" Sending without note..."),await ot(e,g)?(console.log(" \u2713 Connection sent (without note)"),l.success=!0,l.connectionSent=!0,l.reason="sent_without_note",l.withCustomMessage=!1,l):(console.log(" \u2717 Send not confirmed \u2014 modal stayed open / error toast; NOT counting as sent"),l.reason="modal_failed",l);if(h&&!r){console.log(" No message provided, sending without note...");let y=await e.$('button[aria-label="Send without a note"]');if(y)return await ot(e,y)?(console.log(" \u2713 Connection sent (without note)"),l.success=!0,l.connectionSent=!0,l.reason="sent_without_note",l.withCustomMessage=!1,l):(console.log(" \u2717 Send not confirmed \u2014 modal stayed open / error toast; NOT counting as sent"),l.reason="modal_failed",l)}}else{console.log(" Sending without note (custom messages disabled)...");let y=g;if(y)return await ot(e,y)?(console.log(" \u2713 Connection sent (without note)"),l.success=!0,l.connectionSent=!0,l.reason="sent_without_note",l.withCustomMessage=!1,l):(console.log(" \u2717 Send not confirmed \u2014 modal stayed open / error toast; NOT counting as sent"),l.reason="modal_failed",l)}if(d.method!=="more_dropdown"){console.log(" \u26A0 No modal after click \u2014 trying More dropdown fallback..."),e.url().includes("/in/")||(await e.goto(t,{waitUntil:"domcontentloaded",timeout:6e4}),await w(4e3)),await oe(e),await w(500);let S=await e.$$('button[id*="profile-overflow-action"]'),x=null;for(let b of S){let v=await e.evaluate(L=>{let D=L.getBoundingClientRect();return{visible:D.width>0&&D.height>0,inSticky:!!L.closest('.sticky-header, [class*="sticky"]')}},b);if(v.visible&&!v.inSticky){x=b;break}}!x&&S.length>0&&(x=S[S.length-1]);for(let b of S)b!==x&&await b.dispose().catch(()=>{});if(x)try{await e.evaluate(v=>v.scrollIntoView({behavior:"smooth",block:"center"}),x),await w(500),await x.click().catch(()=>e.evaluate(v=>v.click(),x)),await w(2e3);let b=await e.$('.artdeco-dropdown__item[aria-label*="Invite"]')||await e.$('.artdeco-dropdown__item[aria-label*="connect" i]:not([aria-label*="remove" i]):not([aria-label*="disconnect" i])')||await e.$('div[role="button"][aria-label*="Invite"]')||await e.$('div[role="button"][aria-label*="connect" i]:not([aria-label*="remove" i]):not([aria-label*="disconnect" i])');if(b){console.log(" Found Connect in More dropdown, clicking..."),l.method="more_dropdown",await e.evaluate(P=>P.click(),b),await w(3e3);let v=e.url();if(v.includes("/premium/")||v.includes("/redeem"))return console.log(" \u2717 More dropdown also redirected to Premium"),await e.goto(t,{waitUntil:"domcontentloaded",timeout:6e4}),await w(3e3),await tt(e)==="pending"?(console.log(" \u2713 Connection was sent (detected Pending)"),l.success=!0,l.connectionSent=!0,l.reason="sent_without_note",l.withCustomMessage=!1,l):(l.reason="premium_required",l);if(await tt(e)==="pending")return console.log(" \u2713 Connection sent via More dropdown"),l.success=!0,l.connectionSent=!0,l.reason="sent_without_note",l.withCustomMessage=!1,l;let D=await e.$('button[aria-label="Send without a note"]');if(D)return console.log(" Modal appeared via More dropdown, sending without note..."),await ot(e,D)?(console.log(" \u2713 Connection sent (without note, via More dropdown)"),l.success=!0,l.connectionSent=!0,l.reason="sent_without_note",l.withCustomMessage=!1,l):(console.log(" \u2717 Send not confirmed (More-dropdown modal)"),l.reason="modal_failed",l);console.log(" More dropdown: no modal either")}else console.log(" No Connect option in More dropdown"),await e.keyboard.press("Escape")}catch(b){console.log(" More dropdown fallback failed:",b.message),await e.keyboard.press("Escape").catch(()=>{})}}return console.log(" \u2717 Could not complete connection request"),l.reason="modal_failed",l}catch(u){console.error(` \u2717 Error: ${u.message}`),l.reason="error",l.error=u.message;let c=u.message.toLowerCase();if(c.includes("detached")||c.includes("session closed")||c.includes("target closed")||c.includes("browser has disconnected")||c.includes("protocol error"))return l.fatal=!0,l;try{await e.goto("about:blank",{timeout:1e4}),await w(1e3)}catch{l.fatal=!0}return l}}async function Ro(e,t,o={}){let{message:r=null,useCustomMessage:n=!0,maxConnections:i=20,viewOnly:s=!1,delayBetweenProfiles:a=3e3,onProfileProcessed:l=null,excludeLocations:u=[],source:c="unknown"}=o,f={viewed:0,connectionsSent:0,skipped:{pending:0,connected:0,email:0,notFound:0,excludedLocation:0},errors:0};if(console.log(`
246
+ --- Connecting: ${u} ---`),a.length>0){let y=Q()[t]?.location;if(tr(y,a))return console.log(` \u26A0 Excluded location (stored): ${y}`),l.reason="excluded_location",l.profileInfo={location:y},l}if(await e.goto(t,{waitUntil:"domcontentloaded",timeout:6e4}),await w(5e3),await oe(e)&&(console.log(" Dismissed Premium overlay"),await w(2e3),await or(e,t),await w(2e3)),["true","1"].includes(process.env.DISCOVERY_ENABLED))try{let{extractSidePanelProfiles:y,normalizeProfileUrl:S}=await Promise.resolve().then(()=>(go(),jn)),{recordDiscoveredEdges:x}=await Promise.resolve().then(()=>(Jt(),qn)),{detectCategoryFromTitle:b}=await Qn().then(()=>zn),{detectSectors:v}=await Promise.resolve().then(()=>(er(),Zn)),L=S(t)||t,D=await y(e,{seedUrl:L}),P=D.map(E=>{let _=E.headline?b(E.headline):null;return{url:E.url,seedUrl:L,block:E.block,rank:E.rank,name:E.name,headline:E.headline,categories:_?[_]:[],sectors:v(E.headline||"")}});x(P),D.length&&console.log(` \xB7 discovery: harvested ${D.length} related profiles`)}catch(y){["true","1"].includes(process.env.DISCOVERY_DEBUG)&&console.log(` \xB7 discovery harvest skipped: ${y.message}`)}l.profileInfo=await e.evaluate(()=>{let y=new Set(["Highlights","About","Activity","Experience","Education","Skills","Recommendations","Languages","Interests","Volunteer experience","Featured","Honors & awards","Honors and awards","Projects","Publications","Patents","Certifications","Courses","Test scores","Organizations","More profiles for you","Explore Premium profiles","Suggested for you","Promoted","Ad Options","Don\u2019t want to see this"]),S=C=>{let M=(C||"").trim().replace(/^[·•]\s*/,"");return/^(1st|2nd|3rd|she\/her|he\/him|they\/them|their\/them)$/i.test(M)},x={name:null,title:null,location:null,about:null,company:null,connectionDegree:null,connectionsCount:null,mutualConnections:null,photoUrl:null,hasVerifiedBadge:!1},b=document.querySelector("main");if(!b)return x;let v=[...Array.from(b.querySelectorAll("h1")),...Array.from(b.querySelectorAll("h2"))],L=Array.from(b.querySelectorAll("h2")),D=v.find(C=>{let M=C.textContent&&C.textContent.trim();return M&&M.length>1&&M.length<100&&!y.has(M)&&M!=="0 notifications"}),P=D?D.textContent.trim():null,E=D?D.parentElement:null;for(;E&&E!==b&&!Array.from(E.children).filter(q=>q.tagName==="P").some(q=>{let I=q.textContent&&q.textContent.trim();return I&&I.length>5&&!S(I)});)E=E.parentElement;let _=null,K=null,Y=null;if(E&&E!==b){let C=Array.from(E.children),M=C.filter(I=>I.tagName==="P").filter(I=>{let G=I.textContent&&I.textContent.trim();return G&&G.length>5&&!S(G)});M[0]&&(_=M[0].textContent.trim()),M[1]&&(K=M[1].textContent.trim());let q=C.filter(I=>I.tagName==="DIV");for(let I=q.length-1;I>=0;I--){let G=q[I].textContent&&q[I].textContent.trim();if(G&&!(P&&G.startsWith(P))&&(Y=G.split(/[·•]/)[0].trim(),Y))break}}let W=null;if(E){let C=E.querySelectorAll("p, span, div");for(let M of C){let q=(M.textContent||"").trim().replace(/^[·•]\s*/,"");if(q==="1st"||q==="2nd"||q==="3rd"){W=q;break}}}let V=null,T=L.find(C=>C.textContent&&C.textContent.trim()==="About");if(T){let C=T.parentElement;for(;C&&C.tagName!=="SECTION";)C=C.parentElement;C&&(V=(C.textContent||"").trim().replace(/^About\s*/,"").trim()||null,V&&(V=V.replace(/(?:…|\.\.\.)?\s*(?:see (?:more|less)|show (?:more|less))\s*$/i,"").trim()||null))}let B=document.querySelector('main img[width="200"], button img[alt*="photo" i]'),R=B&&B.src||null,O=!!b.querySelector('svg[data-test-icon="verified"], [aria-label*="Verified" i]');return{name:P,title:_,location:Y,about:V,company:K,connectionDegree:W,connectionsCount:null,mutualConnections:null,photoUrl:R,hasVerifiedBadge:O}});try{let y=e.url();y&&/\/in\/[^/?#]/.test(y)&&(l.resolvedUrl=y.split("?")[0].replace(/\/+$/,""))}catch{}if(a.length>0){let y=l.profileInfo?.location,S=Q()[t]?.location,x=y||S;if(tr(x,a))return console.log(` \u26A0 Excluded location (${y?"live":"stored"}): ${x}`),l.reason="excluded_location",l;!y&&S==null&&console.log(" \u26A0 Location unknown (live scrape null, no stored value) \u2014 proceeding")}if((await tt(e,{detail:!0})).status==="pending"&&i)return console.log(" \u26A0 Already pending - skipping"),l.reason="pending",l;await oe(e);let d=await To(e);if(d||await oe(e)&&(console.log(" Dismissed overlay, retrying connect button search..."),await w(2e3),d=await To(e)),d&&d.connected)return console.log(" \u26A0 Already connected (Remove connection in More menu) - skipping"),l.reason="connected",l;if(!d){let y=await tt(e,{detail:!0});return s&&(y.status==="connected"||y.debug?.hasMessageBtn)?(console.log(" \u26A0 Already connected (no Connect control found, incl. under More) - skipping"),l.reason="connected",l):(console.log(" \u2717 Connect not found"),l.reason="button_not_found",l)}l.method=d.method;let p=await as(e,d.button);if(p==="wrong_target")return console.log(" \u2717 Not sent \u2014 connect control did not belong to this profile"),l.reason="wrong_target",l;if(p==="premium_redirect"){console.log(" \u26A0 Premium upsell redirect \u2014 navigating back to profile..."),await e.goto(t,{waitUntil:"domcontentloaded",timeout:6e4}),await w(4e3);let y=await tt(e);if(y==="pending")return console.log(" \u2713 Connection was sent (detected Pending after upsell redirect)"),l.success=!0,l.connectionSent=!0,l.reason="sent_without_note",l.withCustomMessage=!1,l;console.log(` Status after upsell: ${y} \u2014 trying More dropdown...`),await oe(e),await w(1e3);let S=await e.$('button[id*="profile-overflow-action"]:not([class*="sticky"])');if(S)try{await e.evaluate(b=>b.scrollIntoView({behavior:"smooth",block:"center"}),S),await w(500),await S.click().catch(()=>e.evaluate(b=>b.click(),S)),await w(2e3);let x=await e.$('.artdeco-dropdown__item[aria-label*="Invite"]')||await e.$('.artdeco-dropdown__item[aria-label*="connect" i]:not([aria-label*="remove" i]):not([aria-label*="disconnect" i])')||await e.$('div[role="button"][aria-label*="Invite"]')||await e.$('div[role="button"][aria-label*="connect" i]:not([aria-label*="remove" i]):not([aria-label*="disconnect" i])');if(x){console.log(" Found Connect in More dropdown, clicking..."),await e.evaluate(L=>L.click(),x),await w(3e3);let b=e.url();if(b.includes("/premium/")||b.includes("/redeem"))return console.log(" \u2717 More dropdown also redirected to Premium"),await e.goto(t,{waitUntil:"domcontentloaded",timeout:6e4}),await w(3e3),l.reason="premium_required",l;if(await tt(e)==="pending")return console.log(" \u2713 Connection sent via More dropdown"),l.success=!0,l.connectionSent=!0,l.reason="sent_without_note",l.method="more_dropdown",l.withCustomMessage=!1,l;console.log(" More dropdown clicked, checking for modal...")}else return console.log(" No Connect option in More dropdown"),await e.keyboard.press("Escape"),await w(500),l.reason="premium_required",l}catch(x){return console.log(" More dropdown failed:",x.message),await e.keyboard.press("Escape").catch(()=>{}),l.reason="premium_required",l}else return console.log(" \u2717 No More button found \u2014 Premium required"),l.reason="premium_required",l}if(await e.evaluate(()=>{let y=[document,document.querySelector("#interop-outlet")?.shadowRoot].filter(Boolean);for(let S of y){let x=S.querySelector('[role="dialog"], .artdeco-modal, .send-invite');if(!x)continue;let b=(x.innerText||"").toLowerCase();if(/enter their email|verify this member knows you/.test(b)||x.querySelector('input[type="email"], input[name="email"]'))return!0;let v=x.querySelector('button[aria-label="Send without a note"]');if(v&&(v.disabled||v.getAttribute("aria-disabled")==="true"||(v.className||"").includes("disabled")))return!0}return!1})){console.log(" \u26A0 Email required to connect (LinkedIn asks for their email) - skipping");let y=await e.$('button[aria-label="Dismiss"]');if(y)try{await y.click()}catch{}else try{await e.keyboard.press("Escape")}catch{}return await w(500),l.reason="email_required",l}let h=await e.$('button[aria-label="Add a note"]'),g=await e.$('button[aria-label="Send without a note"]');if(!h&&!g){let y=await e.evaluate(()=>{let S=document.querySelector("#interop-outlet")?.shadowRoot;if(!S)return{addNote:!1,sendWithout:!1};let x=S.querySelector('button[aria-label="Add a note"]'),b=S.querySelector('button[aria-label="Send without a note"]');return{addNote:!!x,sendWithout:!!b}});if(y.addNote){let S=await e.evaluateHandle(()=>document.querySelector("#interop-outlet")?.shadowRoot?.querySelector('button[aria-label="Add a note"]'));h=S?.asElement?.()||null,h||await S.dispose().catch(()=>{})}if(y.sendWithout){let S=await e.evaluateHandle(()=>document.querySelector("#interop-outlet")?.shadowRoot?.querySelector('button[aria-label="Send without a note"]'));g=S?.asElement?.()||null,g||await S.dispose().catch(()=>{})}}if(n)if(h&&r){if(console.log(" Adding personalized note..."),await h.click(),await w(1500),await e.$("div.modal-upsell")){let x=await cs(e);return l.success=x,l.connectionSent=x,l.reason=x?"sent_without_note_upsell":"upsell_failed",l.withCustomMessage=!1,l}let S=await e.$('textarea[name="message"]')||await e.$("textarea#custom-message")||await e.$("textarea");if(S){let x=await e.evaluate(()=>document.querySelector("h1")?.textContent?.trim()?.split(" ")[0]||""),b=r.replace("{name}",x);await S.type(b,{delay:20}),await w(500);let v=await e.$('button[aria-label="Send invitation"]')||await e.$('button[aria-label="Send now"]');if(v)return await ot(e,v)?(console.log(" \u2713 Connection sent (with note)"),l.success=!0,l.connectionSent=!0,l.reason="sent_with_note",l.withCustomMessage=!0,l.messageSent=b,l):(console.log(" \u2717 Send not confirmed \u2014 modal stayed open / error toast; NOT counting as sent"),l.reason="modal_failed",l)}}else{if(g)return console.log(" Sending without note..."),await ot(e,g)?(console.log(" \u2713 Connection sent (without note)"),l.success=!0,l.connectionSent=!0,l.reason="sent_without_note",l.withCustomMessage=!1,l):(console.log(" \u2717 Send not confirmed \u2014 modal stayed open / error toast; NOT counting as sent"),l.reason="modal_failed",l);if(h&&!r){console.log(" No message provided, sending without note...");let y=await e.$('button[aria-label="Send without a note"]');if(y)return await ot(e,y)?(console.log(" \u2713 Connection sent (without note)"),l.success=!0,l.connectionSent=!0,l.reason="sent_without_note",l.withCustomMessage=!1,l):(console.log(" \u2717 Send not confirmed \u2014 modal stayed open / error toast; NOT counting as sent"),l.reason="modal_failed",l)}}else{console.log(" Sending without note (custom messages disabled)...");let y=g;if(y)return await ot(e,y)?(console.log(" \u2713 Connection sent (without note)"),l.success=!0,l.connectionSent=!0,l.reason="sent_without_note",l.withCustomMessage=!1,l):(console.log(" \u2717 Send not confirmed \u2014 modal stayed open / error toast; NOT counting as sent"),l.reason="modal_failed",l)}if(d.method!=="more_dropdown"){console.log(" \u26A0 No modal after click \u2014 trying More dropdown fallback..."),e.url().includes("/in/")||(await e.goto(t,{waitUntil:"domcontentloaded",timeout:6e4}),await w(4e3)),await oe(e),await w(500);let S=await e.$$('button[id*="profile-overflow-action"]'),x=null;for(let b of S){let v=await e.evaluate(L=>{let D=L.getBoundingClientRect();return{visible:D.width>0&&D.height>0,inSticky:!!L.closest('.sticky-header, [class*="sticky"]')}},b);if(v.visible&&!v.inSticky){x=b;break}}!x&&S.length>0&&(x=S[S.length-1]);for(let b of S)b!==x&&await b.dispose().catch(()=>{});if(x)try{await e.evaluate(v=>v.scrollIntoView({behavior:"smooth",block:"center"}),x),await w(500),await x.click().catch(()=>e.evaluate(v=>v.click(),x)),await w(2e3);let b=await e.$('.artdeco-dropdown__item[aria-label*="Invite"]')||await e.$('.artdeco-dropdown__item[aria-label*="connect" i]:not([aria-label*="remove" i]):not([aria-label*="disconnect" i])')||await e.$('div[role="button"][aria-label*="Invite"]')||await e.$('div[role="button"][aria-label*="connect" i]:not([aria-label*="remove" i]):not([aria-label*="disconnect" i])');if(b){console.log(" Found Connect in More dropdown, clicking..."),l.method="more_dropdown",await e.evaluate(P=>P.click(),b),await w(3e3);let v=e.url();if(v.includes("/premium/")||v.includes("/redeem"))return console.log(" \u2717 More dropdown also redirected to Premium"),await e.goto(t,{waitUntil:"domcontentloaded",timeout:6e4}),await w(3e3),await tt(e)==="pending"?(console.log(" \u2713 Connection was sent (detected Pending)"),l.success=!0,l.connectionSent=!0,l.reason="sent_without_note",l.withCustomMessage=!1,l):(l.reason="premium_required",l);if(await tt(e)==="pending")return console.log(" \u2713 Connection sent via More dropdown"),l.success=!0,l.connectionSent=!0,l.reason="sent_without_note",l.withCustomMessage=!1,l;let D=await e.$('button[aria-label="Send without a note"]');if(D)return console.log(" Modal appeared via More dropdown, sending without note..."),await ot(e,D)?(console.log(" \u2713 Connection sent (without note, via More dropdown)"),l.success=!0,l.connectionSent=!0,l.reason="sent_without_note",l.withCustomMessage=!1,l):(console.log(" \u2717 Send not confirmed (More-dropdown modal)"),l.reason="modal_failed",l);console.log(" More dropdown: no modal either")}else console.log(" No Connect option in More dropdown"),await e.keyboard.press("Escape")}catch(b){console.log(" More dropdown fallback failed:",b.message),await e.keyboard.press("Escape").catch(()=>{})}}return console.log(" \u2717 Could not complete connection request"),l.reason="modal_failed",l}catch(u){console.error(` \u2717 Error: ${u.message}`),l.reason="error",l.error=u.message;let c=u.message.toLowerCase();if(c.includes("detached")||c.includes("session closed")||c.includes("target closed")||c.includes("browser has disconnected")||c.includes("protocol error"))return l.fatal=!0,l;try{await e.goto("about:blank",{timeout:1e4}),await w(1e3)}catch{l.fatal=!0}return l}}async function Ro(e,t,o={}){let{message:r=null,useCustomMessage:n=!0,maxConnections:i=20,viewOnly:s=!1,delayBetweenProfiles:a=3e3,onProfileProcessed:l=null,excludeLocations:u=[],source:c="unknown"}=o,f={viewed:0,connectionsSent:0,skipped:{pending:0,connected:0,email:0,notFound:0,excludedLocation:0},errors:0};if(console.log(`
247
247
  === Running Funnel: ${t.length} profiles ===`),console.log(`Mode: ${s?"View Only":`Connect (max ${i})`}`),s||console.log(`Custom messages: ${n?"Enabled":"Disabled (saving quota)"}`),u.length>0&&console.log(`Excluding locations: ${u.join(", ")}`),!s){let p=dt();console.log(`Quota: today ${p.daily.sent}/${p.daily.max} | week ${p.weekly.sent}/${p.weekly.max}`)}console.log("");let d=0;for(let p=0;p<t.length;p++){let m=t[p];try{if(e.isClosed()){console.log(`
248
248
  \u26A0 Browser page is closed \u2014 aborting funnel`);break}}catch{console.log(`
249
249
  \u26A0 Browser disconnected \u2014 aborting funnel`);break}if(!m.profileUrl){console.log(`[${p+1}/${t.length}] Skipping - no URL`);continue}let h=Dt(m.profileUrl);if(h==="pending"||h==="connected"||h==="sent_with_note"||h==="sent_without_note"||h==="sent_without_note_upsell"){console.log(`[${p+1}/${t.length}] ${m.name||m.profileUrl} \u2014 skipped (db: ${h})`),h==="pending"||h==="sent_with_note"||h==="sent_without_note"||h==="sent_without_note_upsell"?f.skipped.pending++:f.skipped.connected++,l&&await l(m,f,{success:!1,reason:h,connectionSent:!1,withCustomMessage:!1,messageSent:null,method:null,error:null,profileInfo:null});continue}console.log(`[${p+1}/${t.length}] ${m.name||m.profileUrl}`);let g=null;if(s){let y=await ls(e,m.profileUrl);y?(Ce(m.profileUrl,{...y,source:c}),f.viewed++,g={success:!0,reason:"viewed",connectionSent:!1,profileInfo:y,resolvedUrl:y.resolvedUrl}):(f.errors++,g={success:!1,reason:"view_failed",connectionSent:!1,profileInfo:null})}else{if(f.connectionsSent>=i){console.log(` Max connections per session (${i}) reached`);break}let y=ut();if(!y.allowed){console.log(` \u26A0 Global quota exhausted: ${y.reason}`);break}if(g=await us(e,m.profileUrl,{message:r,useCustomMessage:n,excludeLocations:u,source:c,skipQuotaGate:!0}),g.success)f.connectionsSent++;else switch(g.reason){case"pending":f.skipped.pending++;break;case"connected":f.skipped.connected++;break;case"email_required":f.skipped.email++;break;case"button_not_found":f.skipped.notFound++;break;case"excluded_location":f.skipped.excludedLocation++;break;default:f.errors++}}if(g?.fatal){console.log(`