ilml-plugin-linkedin 1.11.2 → 1.11.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/visit.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  var At=Object.defineProperty;var Ne=(e,t)=>{for(var o in t)At(e,o,{get:t[o],enumerable:!0})};import jt from"puppeteer";import{addExtra as Ot}from"puppeteer-extra";import Nt from"puppeteer-extra-plugin-stealth";async function R(e){let t=Math.floor(Math.random()*2001),o=e+t;return new Promise(n=>setTimeout(n,o))}async function Le(e){return await e.evaluate(()=>{let t=document.querySelectorAll("div.artdeco-modal--is-open");for(let n of t){let s=n.innerText||"",r=s.includes("Premium")||s.includes("premium")||s.includes("Upgrade")||s.includes("upgrade"),i=s.includes("Try for")||s.includes("free trial")||s.includes("/month")||s.includes("per month")||s.includes("Start my")||s.includes("Subscribe"),l=s.includes("Get hired faster")||s.includes("Get hired 2x");if(r||i||l){let c=n.querySelector('button[aria-label="Dismiss"]')||n.querySelector("button.artdeco-modal__dismiss")||n.querySelector("button[data-test-modal-close-btn]");if(c)return c.click(),{dismissed:!0,matched:s.substring(0,80)};let a=n.querySelectorAll("button");for(let m of a){let w=m.innerText?.trim().toLowerCase()||"",d=m.getAttribute("aria-label")?.toLowerCase()||"";if(w==="not now"||w==="no thanks"||w==="dismiss"||d.includes("dismiss"))return m.click(),{dismissed:!0,matched:s.substring(0,80)}}}}let o=document.querySelector(".premium-upsell-link")||document.querySelector("[data-test-premium-upsell]")||document.querySelector(".premium-hub-upsell");if(o){let n=o.closest('[class*="modal"]')?.querySelector('button[aria-label="Dismiss"]')||o.closest('[class*="banner"]')?.querySelector('button[aria-label="Close"]');if(n)return n.click(),{dismissed:!0,matched:"premium-banner"}}return!1})}import{getPluginConfig as xt}from"@ilivemylife/graph-sdk";var Et=await xt();for(let[e,t]of Object.entries(Et))process.env[e]===void 0&&(process.env[e]=t);var _t=process.env.LINKEDIN_NAME,q=Object.freeze({DRAFT:"draft",CONFIRMED:"confirmed",REJECTED:"rejected",NEEDS_REVIEW:"needs_review",SENT:"sent"}),en=Object.freeze(new Set([q.DRAFT,q.CONFIRMED,q.NEEDS_REVIEW])),It=Object.freeze(new Set([q.DRAFT,q.CONFIRMED,q.REJECTED,q.NEEDS_REVIEW]));import Fe from"fs";import Tt from"path";var Dt="cookies.json";function Rt(){let e=process.env.ILML_SCOPE_DIR;return e?Tt.join(e,"plugins-state","linkedin","cookies.json"):Dt}function Me(){let e=Rt();if(!Fe.existsSync(e))throw new Error(`Cookies not found at ${e}. Run: ilml linkedin login`);let t;try{t=Fe.readFileSync(e,"utf-8")}catch(n){throw new Error(`Failed to read cookies at ${e}: ${n.message}. Run: ilml linkedin login`)}let o;try{o=JSON.parse(t)}catch{throw new Error(`Cookies file at ${e} is corrupted (invalid JSON). Run: ilml linkedin login`)}if(!Array.isArray(o)||o.length===0)throw new Error(`Cookies file at ${e} is empty or not an array. Run: ilml linkedin login`);return o}var Pe=Ot(jt);Pe.use(Nt());async function qe(){let e=await Pe.launch({headless:!1,defaultViewport:null,protocolTimeout:3e5}),t=async()=>{console.log(`
2
2
  Closing browser...`);try{await e.close()}catch{}process.exit(0)};process.on("SIGINT",t),process.on("SIGTERM",t);let o=await e.newPage();await o.evaluateOnNewDocument(()=>{Object.defineProperty(navigator,"webdriver",{get:()=>{}})});try{let n=Me();await o.setCookie(...n),console.log(" Browser launched, cookies loaded.")}catch(n){console.error(`
3
3
  [!] ${n.message}
4
- `),await e.close(),process.exit(1)}return{browser:e,page:o}}import"dotenv/config";import*as v from"fs";import*as C from"path";import{fileURLToPath as Lt}from"url";var Ft=Lt(import.meta.url),Mt=C.dirname(C.dirname(Ft)),O=process.env.DATA_DIR?C.resolve(process.env.DATA_DIR,"collected-profiles"):C.join(Mt,"collected-profiles"),le=C.join(O,"people.json"),wn=C.join(O,"jobs.json"),bn=C.join(O,"quota.json"),Sn=C.join(O,"conversations.json"),vn=C.join(O,"sync-state.json"),kn=C.join(O,"application-questions.json"),ae=C.join(O,"profile-history");var G=null,Pt=null,qt=null,Ut=null,Bt=null,Vt=null;function Ve(){G=null,Pt=null,qt=null,Ut=null,Bt=null,Vt=null}function He(){v.existsSync(O)||v.mkdirSync(O,{recursive:!0})}function Je(e,t){let o=JSON.stringify(t,null,2),n=e+".tmp";v.writeFileSync(n,o);for(let s=0;s<3;s++)try{v.renameSync(n,e);return}catch(r){if(s<2&&(r.code==="EPERM"||r.code==="EBUSY")){let i=Date.now();for(;Date.now()-i<200;);continue}throw r}}function We(e,t){if(!v.existsSync(e))return null;try{let o=JSON.parse(v.readFileSync(e,"utf-8"));try{v.copyFileSync(e,e+".backup")}catch{}return o}catch(o){console.error(`[!] ${t} is corrupted: ${o.message}`),console.error(` File: ${C.resolve(e)}`);let n=e+".backup";if(v.existsSync(n))try{let s=JSON.parse(v.readFileSync(n,"utf-8"));console.error(` Restored from ${n}`);try{v.copyFileSync(n,e)}catch{}return s}catch{console.error(" Backup also corrupted!")}console.error(` Starting with empty data. Old file preserved as ${e}.corrupted`);try{v.copyFileSync(e,e+".corrupted")}catch{}return null}}function Ht(){return new Date().toISOString().split("T")[0]}var Jt=["name","title","location","company","connectionDegree","connectionsCount","mutualConnections","photoUrl","hasVerifiedBadge","about","currentCompany","currentSchool","connectionsLevel","mutualConnectionsCount","followers","openToWork","hiring","topSkills","availableSections"],Wt=["name","title","location","currentCompany","currentSchool","connectionDegree","hasVerifiedBadge","openToWork","hiring","about","topSkills"];function ce(e){if(!e)return null;let t=e.match(/\/in\/([^/?#]+)/);return t?t[1].toLowerCase():null}function Gt(){v.existsSync(ae)||v.mkdirSync(ae,{recursive:!0})}function Ge(e){let t=ce(e);return t?C.join(ae,`${t}.json`):null}function Yt(e){let t=Ge(e);return t?We(t,`profile-history/${ce(e)}.json`)||[]:[]}function zt(e,t){let o=Ge(e);if(!o)return;Gt();let n=Yt(e);n.unshift(t);try{Je(o,n)}catch(s){console.error(`[!] Failed to append profile-history for ${ce(e)}: ${s.message}`)}}function Ue(e){return!!(e==null||Array.isArray(e)&&e.length===0||typeof e=="string"&&!e.trim())}function Be(e){return Array.isArray(e)?[...e].map(t=>String(t).trim()).sort().join("||"):typeof e=="string"?e.replace(/\s+/g," ").trim():e}function Qt(e,t){if(!e)return[];let o=[];for(let n of Wt){let s=e[n],r=t[n];Ue(s)||Ue(r)||Be(s)!==Be(r)&&o.push(n)}return o}function Kt(e){let t=new Set(["seenCount","sources","firstSeen","lastSeen","lastUpdated","encounters","jobIds","lastFullVisitAt","lastSnapshotAt","messageSent","connectionStatus","connectionDate","connectionMethod"]),o={};for(let[n,s]of Object.entries(e))t.has(n)||(o[n]=s);return o}function Q(){return G||(He(),G=We(le,"people.json")||{},G)}function Zt(e){He(),G=e;try{Je(le,e)}catch(t){console.error(`[!] Failed to save people.json: ${t.message}`),console.error(` Path: ${C.resolve(le)}`)}}function ue(e,t={}){if(!e)return null;let o=Q(),n=Ht(),s=new Date().toISOString(),r=o[e];if(r){let i=Qt(r,t);i.length>0&&(zt(e,{capturedAt:s,previousLastUpdated:r.lastUpdated||null,changedFields:i,snapshot:Kt(r)}),r.lastSnapshotAt=s),r.seenCount=(r.seenCount||1)+1,r.lastSeen=n,r.lastUpdated=s,t.source&&!r.sources?.includes(t.source)&&(r.sources=r.sources||[],r.sources.push(t.source));for(let l of Jt)t[l]!=null&&(r[l]=t[l]);Array.isArray(t.availableSections)&&(r.lastFullVisitAt=s)}else o[e]={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,firstSeen:n,lastSeen:n,seenCount:1,sources:t.source?[t.source]:[],connectionStatus:null,connectionDate:null,connectionMethod:null,messageSent:null,jobIds:[],lastUpdated:s,lastSnapshotAt:null,lastFullVisitAt:Array.isArray(t.availableSections)?s:null};return Zt(o),o[e]}async function Xt(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: npm run login");console.log(` Off-track: ${o.slice(0,80)}`),console.log(" Navigating back to profile..."),await e.goto(t,{waitUntil:"domcontentloaded",timeout:6e4}),await R(3e3);let n=e.url();return n.includes("/in/")||console.log(` Recovery failed \u2014 still on: ${n.slice(0,80)}`),!0}async function Ye(e,t){try{await e.goto(t,{waitUntil:"domcontentloaded",timeout:6e4}),await R(3e3),await Le(e)&&(console.log(" Dismissed Premium overlay"),await R(1500),await Xt(e,t));let n=0;for(let r=0;r<2;r++){let i=await e.evaluate(()=>document.body.scrollHeight);for(let c=1;c<=6;c++)await e.evaluate(a=>window.scrollTo(0,a),i*c/6),await R(900);let l=await e.evaluate(()=>document.body.scrollHeight);if(l===n)break;n=l}await R(800);let s=await e.evaluate(()=>{let r=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"]),i=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=f=>{let h=(f||"").trim().replace(/^[·•]\s*/,"");return/^(1st|2nd|3rd|she\/her|he\/him|they\/them|their\/them)$/i.test(h)},c={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:[]},a=document.querySelector("main");if(!a)return c;let m=[...Array.from(a.querySelectorAll("h1")),...Array.from(a.querySelectorAll("h2"))],w=Array.from(a.querySelectorAll("h2")),d=m.find(f=>{let h=f.textContent&&f.textContent.trim(),b=(h||"").replace(/\s*\(\d+\)$/,"").trim();return h&&h.length>1&&h.length<100&&!r.has(b)&&!i.has(b)}),y=d?d.textContent.trim():null,k=d?d.parentElement:null;for(;k&&k!==a&&!Array.from(k.children).filter(h=>h.tagName==="P").some(h=>{let b=h.textContent&&h.textContent.trim();return b&&b.length>5&&!l(b)});)k=k.parentElement;let $=null,D=null,g=null,p=null;if(k&&k!==a){let f=Array.from(k.children).filter(b=>b.tagName==="P").filter(b=>{let F=b.textContent&&b.textContent.trim();return F&&F.length>5&&!l(F)});if(f[0]&&($=f[0].textContent.trim()),f[1]){let b=f[1].textContent.trim().split(/\s*[·•]\s*/);D=b[0]||null,b.length>1&&(g=b.slice(1).join(" \xB7 "))}let h=Array.from(k.children).filter(b=>b.tagName==="DIV");for(let b=h.length-1;b>=0;b--){let F=h[b].textContent&&h[b].textContent.trim();if(F&&!(y&&F.startsWith(y))&&(p=F.split(/[·•]/)[0].trim(),p))break}}let L=null;if(k){let f=k.querySelectorAll("p, span, div");for(let h of f){let b=(h.textContent||"").trim().replace(/^[·•]\s*/,"");if(b==="1st"||b==="2nd"||b==="3rd"){L=b;break}}}let wt=!!a.querySelector('svg[data-test-icon="verified"], [aria-label*="Verified" i]'),Ae=document.querySelector('main img[width="200"], button img[alt*="photo" i]'),bt=Ae&&Ae.src||null,ee=a.textContent||"",xe=null,Ee=ee.match(/(\d+\+?)\s+connections\b/i);Ee&&(xe=Ee[1]+" connections");let te=null,_e=ee.match(/(\d+)\s+mutual connection/i),Ie=ee.match(/and\s+(\d+)\s+other mutual/i),St=ee.match(/([A-Z][\w'-]+(?:\s+[A-Z][\w'-]+)?)\s+and\s+([A-Z][\w'-]+(?:\s+[A-Z][\w'-]+)?)\s+are\s+mutual/);_e?te=parseInt(_e[1],10):Ie?te=2+parseInt(Ie[1],10):St&&(te=2);let Te=null,De=null,Re=w.find(f=>f.textContent&&f.textContent.trim()==="About");if(Re){let f=Re.parentElement;for(;f&&f.tagName!=="SECTION";)f=f.parentElement;if(f){let h=(f.textContent||"").trim();h=h.replace(/^About\s*/,"").trim();let b=h.match(/Top skills\s*(.+?)$/i);if(b){let ie=b[1].split(/\b(?:see (?:more|less)|show (?:more|less))\b/i)[0].trim().split(/\s*[·•]\s*/).map($t=>$t.trim()).filter(Boolean);ie.length>0&&ie.length<10&&(De=ie)}h=h.split(/\n?…\s*more/i)[0].trim(),h=h.split(/\n?\.\.\.\s*more/i)[0].trim(),h=h.split(/\bTop skills\b/i)[0].trim(),h=h.split(/\b(?:see (?:more|less)|show (?:more|less))\b/i)[0].trim(),Te=h||null}}let je=null,Oe=w.find(f=>f.textContent&&f.textContent.trim()==="Activity");if(Oe){let f=Oe.parentElement;for(;f&&f.tagName!=="SECTION";)f=f.parentElement;if(f){let h=(f.textContent||"").slice(0,200).match(/([\d,]+)\s+followers/i);h&&(je=parseInt(h[1].replace(/,/g,""),10))}}let vt=[...new Set(w.map(f=>(f.textContent||"").trim().replace(/\s*\(\d+\)$/,"").trim()).filter(f=>f&&f.length>1&&f.length<60&&f!==y).filter(f=>r.has(f)))],z=document.body.textContent||"",kt=/#OpenToWork\b/i.test(z)||/\bOpen to work\b/.test(z),Ct=/#Hiring\b/i.test(z)||/\bI'?m hiring\b/i.test(z)||/\bWe are hiring\b/i.test(z);return{name:y,title:$,location:p,currentCompany:D,currentSchool:g,connectionDegree:L,hasVerifiedBadge:wt,photoUrl:bt,connectionsLevel:xe,mutualConnectionsCount:te,followers:je,openToWork:kt,hiring:Ct,about:Te,topSkills:De,availableSections:vt}});return console.log(`Viewed: ${s.name} - ${s.title}`),{...s,profileUrl:t}}catch(o){console.error(`Error viewing profile ${t}:`,o.message);try{await e.goto("about:blank",{timeout:1e4}),await R(1e3)}catch{}return null}}import"dotenv/config";import*as E from"fs";import*as x from"path";import{fileURLToPath as eo}from"url";var to=eo(import.meta.url),oo=x.dirname(x.dirname(to)),B=process.env.DATA_DIR?x.resolve(process.env.DATA_DIR,"market-research"):x.join(oo,"market-research"),no=x.join(B,"scouted-jobs.json"),so=x.join(B,"discovered-people.json"),ze=x.join(B,"visit-log.json"),Nn=x.join(B,"companies.json"),Ln=x.join(B,"job-decisions.json");function ro(){E.existsSync(B)||E.mkdirSync(B,{recursive:!0})}function io(e,t){let o=JSON.stringify(t,null,2),n=e+".tmp";E.writeFileSync(n,o);for(let s=0;s<3;s++)try{E.renameSync(n,e);return}catch(r){if(s<2&&(r.code==="EPERM"||r.code==="EBUSY")){let i=Date.now();for(;Date.now()-i<200;);continue}throw r}}function de(e){try{if(E.existsSync(e))return JSON.parse(E.readFileSync(e,"utf-8"))}catch{}return null}function Qe(){K=null,ao=null,Z=null,co=null,U=null}var K=null;function lo(){return K||(K=de(no)||{},K)}var ao=null;var Z=null;function oe(){return Z||(Z=de(so)||{},Z)}var co=null;var U=null;function Y(){return U||(U=de(ze)||{visits:[]},Array.isArray(U.visits)||(U.visits=[]),U)}function Ke(e,t={}){if(!e)return;ro();let o=Y();o.visits.push({date:new Date().toISOString(),profileUrl:e,name:t.name||null,source:t.source||"manual"});let n=Date.now()-90*24*60*60*1e3;o.visits=o.visits.filter(s=>new Date(s.date).getTime()>n);try{io(ze,o)}catch(s){console.error(`[!] Failed to save visit-log.json: ${s.message}`)}}function Ze(e){let t=Y();for(let o=t.visits.length-1;o>=0;o--)if(t.visits[o].profileUrl===e)return t.visits[o].date;return null}function Xe(){let e=lo(),t=oe(),o=Object.values(e),n=Object.values(t),s={};for(let d of o)d.company&&(s[d.company]=(s[d.company]||0)+1);let r=Object.entries(s).sort((d,y)=>y[1]-d[1]).slice(0,5).map(([d,y])=>`${d} (${y})`),i={};for(let d of o)for(let y of d.techStack||[])i[y]=(i[y]||0)+1;let l=Object.entries(i).sort((d,y)=>y[1]-d[1]).slice(0,8).map(([d,y])=>`${d} (${y})`),c=Y(),a=new Date().toISOString().slice(0,10),m=c.visits.filter(d=>d.date.startsWith(a)).length,w=new Set(c.visits.map(d=>d.profileUrl)).size;return{totalJobs:o.length,totalRecruiters:n.length,topCompanies:r,topTech:l,totalVisited:w,todayVisited:m,lastScoutDate:o.length>0?o.sort((d,y)=>(y.scoutedAt||"").localeCompare(d.scoutedAt||""))[0]?.scoutedAt:null}}async function et(e,t,o={}){let{maxVisits:n=50,delayBetween:s=4e3,skipRecentDays:r=7,verbose:i=!1}=o,l={visited:0,skipped:0,errors:0},c=Date.now()-r*24*60*60*1e3;for(let a of t){if(l.visited>=n)break;if(!a.profileUrl)continue;let m=Ze(a.profileUrl);if(m&&new Date(m).getTime()>c){l.skipped++;continue}try{let w=await Ye(e,a.profileUrl),d=a.source||"visit";w&&ue(a.profileUrl,{...w,source:d}),Ke(a.profileUrl,{name:w?.name||a.name||null,source:d}),l.visited++,i?console.log(` [${l.visited}] ${w?.name||a.profileUrl} \u2014 ${w?.title?.slice(0,40)||"?"}`):l.visited%10===0&&console.log(` Visited: ${l.visited}`);let y=Math.random()*4e3;await R(s+y)}catch(w){l.errors++,i&&console.error(` Error visiting ${a.name||a.profileUrl}: ${w.message?.slice(0,60)}`);let d=(w.message||"").toLowerCase();if(d.includes("detached")||d.includes("session closed")||d.includes("target closed")){console.log(" Fatal browser error \u2014 stopping visits");break}}}return l}function tt(e={}){let{limit:t=150,revisitDays:o=14}=e,n=Y(),s=new Set(n.visits?.map(g=>g.profileUrl)||[]),r=new Map;for(let g of n.visits||[]){let p=r.get(g.profileUrl);(!p||g.date>p)&&r.set(g.profileUrl,g.date)}let i=Date.now()-7*24*60*60*1e3,l=Date.now()-o*24*60*60*1e3;function c(g){let p=r.get(g);return p&&new Date(p).getTime()>i}let a=oe(),m=Object.entries(a).filter(([g])=>!s.has(g)).map(([g,p])=>({profileUrl:g,name:p.name,source:"new-discovered",priority:1,jobCount:p.jobIds?.length||0})),w=Q(),d=Object.entries(w).filter(([g,p])=>p.connectionStatus==="connected"||c(g)?!1:(p.jobCount||p.jobIds?.length||0)>=2).map(([g,p])=>({profileUrl:g,name:p.name,source:"active-recruiter",priority:2,jobCount:p.jobCount||p.jobIds?.length||0})),y=Object.entries(w).filter(([g,p])=>{let L=p.connectionStatus;return!(!L||L==="connected"||L==="pending"||L==="sent_without_note"||L==="sent_with_note"||c(g)||(p.jobCount||p.jobIds?.length||0)>=2)}).map(([g,p])=>({profileUrl:g,name:p.name,source:"unconnected",priority:3,jobCount:p.jobCount||p.jobIds?.length||0})),k=[...r.entries()].filter(([g,p])=>new Date(p).getTime()<l).map(([g,p])=>({profileUrl:g,name:null,source:"revisit",priority:4,lastVisit:p})),$=g=>g.sort(()=>Math.random()-.5);return m.sort((g,p)=>p.jobCount-g.jobCount),d.sort((g,p)=>p.jobCount-g.jobCount),$(y),$(k),[...m,...d,...y,...k].slice(0,t)}function ot(){let e=oe();return Object.entries(e).map(([t,o])=>({profileUrl:t,name:o.name,source:"discovered"}))}function fe(){let e=Q();return Object.entries(e).filter(([,t])=>{let o=t.connectionStatus;return o&&o!=="connected"&&o!=="pending"&&o!=="sent_without_note"&&o!=="sent_with_note"}).map(([t,o])=>({profileUrl:t,name:o.name,source:"unconnected"}))}function me(e=14){let t=Y(),o=Date.now()-e*24*60*60*1e3,n=new Map;for(let s of t.visits){let r=n.get(s.profileUrl);(!r||s.date>r.date)&&n.set(s.profileUrl,s)}return[...n.entries()].filter(([,s])=>new Date(s.date).getTime()<o).map(([s,r])=>({profileUrl:s,name:r.name,source:"revisit"}))}import"dotenv/config";import*as _ from"fs";import*as M from"path";import{fileURLToPath as go}from"url";import"dotenv/config";import{createGraphClient as uo,resolveToken as fo}from"@ilivemylife/graph-sdk";import{config as mo}from"dotenv";mo();var nt=fo()||process.env.TOKEN;nt||(console.error(`
4
+ `),await e.close(),process.exit(1)}return{browser:e,page:o}}import"dotenv/config";import*as v from"fs";import*as C from"path";import{fileURLToPath as Lt}from"url";var Ft=Lt(import.meta.url),Mt=C.dirname(C.dirname(Ft)),O=process.env.DATA_DIR?C.resolve(process.env.DATA_DIR,"collected-profiles"):C.join(Mt,"collected-profiles"),le=C.join(O,"people.json"),bn=C.join(O,"jobs.json"),Sn=C.join(O,"quota.json"),vn=C.join(O,"conversations.json"),kn=C.join(O,"sync-state.json"),Cn=C.join(O,"application-questions.json"),ae=C.join(O,"profile-history");var G=null,Pt=null,qt=null,Ut=null,Bt=null,Vt=null;function Ve(){G=null,Pt=null,qt=null,Ut=null,Bt=null,Vt=null}function He(){v.existsSync(O)||v.mkdirSync(O,{recursive:!0})}function Je(e,t){let o=JSON.stringify(t,null,2),n=e+".tmp";v.writeFileSync(n,o);for(let s=0;s<3;s++)try{v.renameSync(n,e);return}catch(r){if(s<2&&(r.code==="EPERM"||r.code==="EBUSY")){let i=Date.now();for(;Date.now()-i<200;);continue}throw r}}function We(e,t){if(!v.existsSync(e))return null;try{let o=JSON.parse(v.readFileSync(e,"utf-8"));try{v.copyFileSync(e,e+".backup")}catch{}return o}catch(o){console.error(`[!] ${t} is corrupted: ${o.message}`),console.error(` File: ${C.resolve(e)}`);let n=e+".backup";if(v.existsSync(n))try{let s=JSON.parse(v.readFileSync(n,"utf-8"));console.error(` Restored from ${n}`);try{v.copyFileSync(n,e)}catch{}return s}catch{console.error(" Backup also corrupted!")}console.error(` Starting with empty data. Old file preserved as ${e}.corrupted`);try{v.copyFileSync(e,e+".corrupted")}catch{}return null}}function Ht(){return new Date().toISOString().split("T")[0]}var Jt=["name","title","location","company","connectionDegree","connectionsCount","mutualConnections","photoUrl","hasVerifiedBadge","about","currentCompany","currentSchool","connectionsLevel","mutualConnectionsCount","followers","openToWork","hiring","topSkills","availableSections"],Wt=["name","title","location","currentCompany","currentSchool","connectionDegree","hasVerifiedBadge","openToWork","hiring","about","topSkills"];function ce(e){if(!e)return null;let t=e.match(/\/in\/([^/?#]+)/);return t?t[1].toLowerCase():null}function Gt(){v.existsSync(ae)||v.mkdirSync(ae,{recursive:!0})}function Ge(e){let t=ce(e);return t?C.join(ae,`${t}.json`):null}function Yt(e){let t=Ge(e);return t?We(t,`profile-history/${ce(e)}.json`)||[]:[]}function zt(e,t){let o=Ge(e);if(!o)return;Gt();let n=Yt(e);n.unshift(t);try{Je(o,n)}catch(s){console.error(`[!] Failed to append profile-history for ${ce(e)}: ${s.message}`)}}function Ue(e){return!!(e==null||Array.isArray(e)&&e.length===0||typeof e=="string"&&!e.trim())}function Be(e){return Array.isArray(e)?[...e].map(t=>String(t).trim()).sort().join("||"):typeof e=="string"?e.replace(/\s+/g," ").trim():e}function Qt(e,t){if(!e)return[];let o=[];for(let n of Wt){let s=e[n],r=t[n];Ue(s)||Ue(r)||Be(s)!==Be(r)&&o.push(n)}return o}function Kt(e){let t=new Set(["seenCount","sources","firstSeen","lastSeen","lastUpdated","encounters","jobIds","lastFullVisitAt","lastSnapshotAt","messageSent","connectionStatus","connectionDate","connectionMethod"]),o={};for(let[n,s]of Object.entries(e))t.has(n)||(o[n]=s);return o}function Q(){return G||(He(),G=We(le,"people.json")||{},G)}function Zt(e){He(),G=e;try{Je(le,e)}catch(t){console.error(`[!] Failed to save people.json: ${t.message}`),console.error(` Path: ${C.resolve(le)}`)}}function ue(e,t={}){if(!e)return null;let o=Q(),n=Ht(),s=new Date().toISOString(),r=o[e];if(r){let i=Qt(r,t);i.length>0&&(zt(e,{capturedAt:s,previousLastUpdated:r.lastUpdated||null,changedFields:i,snapshot:Kt(r)}),r.lastSnapshotAt=s),r.seenCount=(r.seenCount||1)+1,r.lastSeen=n,r.lastUpdated=s,t.source&&!r.sources?.includes(t.source)&&(r.sources=r.sources||[],r.sources.push(t.source));for(let l of Jt)t[l]!=null&&(r[l]=t[l]);Array.isArray(t.availableSections)&&(r.lastFullVisitAt=s)}else o[e]={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,firstSeen:n,lastSeen:n,seenCount:1,sources:t.source?[t.source]:[],connectionStatus:null,connectionDate:null,connectionMethod:null,messageSent:null,jobIds:[],lastUpdated:s,lastSnapshotAt:null,lastFullVisitAt:Array.isArray(t.availableSections)?s:null};return Zt(o),o[e]}async function Xt(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: npm run login");console.log(` Off-track: ${o.slice(0,80)}`),console.log(" Navigating back to profile..."),await e.goto(t,{waitUntil:"domcontentloaded",timeout:6e4}),await R(3e3);let n=e.url();return n.includes("/in/")||console.log(` Recovery failed \u2014 still on: ${n.slice(0,80)}`),!0}async function Ye(e,t){try{await e.goto(t,{waitUntil:"domcontentloaded",timeout:6e4}),await R(3e3),await Le(e)&&(console.log(" Dismissed Premium overlay"),await R(1500),await Xt(e,t));let n=0;for(let r=0;r<2;r++){let i=await e.evaluate(()=>document.body.scrollHeight);for(let c=1;c<=6;c++)await e.evaluate(a=>window.scrollTo(0,a),i*c/6),await R(900);let l=await e.evaluate(()=>document.body.scrollHeight);if(l===n)break;n=l}await R(800);let s=await e.evaluate(()=>{let r=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"]),i=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=f=>{let h=(f||"").trim().replace(/^[·•]\s*/,"");return/^(1st|2nd|3rd|she\/her|he\/him|they\/them|their\/them)$/i.test(h)},c={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:[]},a=document.querySelector("main");if(!a)return c;let m=[...Array.from(a.querySelectorAll("h1")),...Array.from(a.querySelectorAll("h2"))],w=Array.from(a.querySelectorAll("h2")),d=m.find(f=>{let h=f.textContent&&f.textContent.trim(),b=(h||"").replace(/\s*\(\d+\)$/,"").trim();return h&&h.length>1&&h.length<100&&!r.has(b)&&!i.has(b)}),y=d?d.textContent.trim():null,k=d?d.parentElement:null;for(;k&&k!==a&&!Array.from(k.children).filter(h=>h.tagName==="P").some(h=>{let b=h.textContent&&h.textContent.trim();return b&&b.length>5&&!l(b)});)k=k.parentElement;let $=null,D=null,g=null,p=null;if(k&&k!==a){let f=Array.from(k.children).filter(b=>b.tagName==="P").filter(b=>{let F=b.textContent&&b.textContent.trim();return F&&F.length>5&&!l(F)});if(f[0]&&($=f[0].textContent.trim()),f[1]){let b=f[1].textContent.trim().split(/\s*[·•]\s*/);D=b[0]||null,b.length>1&&(g=b.slice(1).join(" \xB7 "))}let h=Array.from(k.children).filter(b=>b.tagName==="DIV");for(let b=h.length-1;b>=0;b--){let F=h[b].textContent&&h[b].textContent.trim();if(F&&!(y&&F.startsWith(y))&&(p=F.split(/[·•]/)[0].trim(),p))break}}let L=null;if(k){let f=k.querySelectorAll("p, span, div");for(let h of f){let b=(h.textContent||"").trim().replace(/^[·•]\s*/,"");if(b==="1st"||b==="2nd"||b==="3rd"){L=b;break}}}let wt=!!a.querySelector('svg[data-test-icon="verified"], [aria-label*="Verified" i]'),Ae=document.querySelector('main img[width="200"], button img[alt*="photo" i]'),bt=Ae&&Ae.src||null,ee=a.textContent||"",xe=null,Ee=ee.match(/(\d+\+?)\s+connections\b/i);Ee&&(xe=Ee[1]+" connections");let te=null,_e=ee.match(/(\d+)\s+mutual connection/i),Ie=ee.match(/and\s+(\d+)\s+other mutual/i),St=ee.match(/([A-Z][\w'-]+(?:\s+[A-Z][\w'-]+)?)\s+and\s+([A-Z][\w'-]+(?:\s+[A-Z][\w'-]+)?)\s+are\s+mutual/);_e?te=parseInt(_e[1],10):Ie?te=2+parseInt(Ie[1],10):St&&(te=2);let Te=null,De=null,Re=w.find(f=>f.textContent&&f.textContent.trim()==="About");if(Re){let f=Re.parentElement;for(;f&&f.tagName!=="SECTION";)f=f.parentElement;if(f){let h=(f.textContent||"").trim();h=h.replace(/^About\s*/,"").trim();let b=h.match(/Top skills\s*(.+?)$/i);if(b){let ie=b[1].split(/\b(?:see (?:more|less)|show (?:more|less))\b/i)[0].trim().split(/\s*[·•]\s*/).map($t=>$t.trim()).filter(Boolean);ie.length>0&&ie.length<10&&(De=ie)}h=h.split(/\n?…\s*more/i)[0].trim(),h=h.split(/\n?\.\.\.\s*more/i)[0].trim(),h=h.split(/\bTop skills\b/i)[0].trim(),h=h.split(/\b(?:see (?:more|less)|show (?:more|less))\b/i)[0].trim(),Te=h||null}}let je=null,Oe=w.find(f=>f.textContent&&f.textContent.trim()==="Activity");if(Oe){let f=Oe.parentElement;for(;f&&f.tagName!=="SECTION";)f=f.parentElement;if(f){let h=(f.textContent||"").slice(0,200).match(/([\d,]+)\s+followers/i);h&&(je=parseInt(h[1].replace(/,/g,""),10))}}let vt=[...new Set(w.map(f=>(f.textContent||"").trim().replace(/\s*\(\d+\)$/,"").trim()).filter(f=>f&&f.length>1&&f.length<60&&f!==y).filter(f=>r.has(f)))],z=document.body.textContent||"",kt=/#OpenToWork\b/i.test(z)||/\bOpen to work\b/.test(z),Ct=/#Hiring\b/i.test(z)||/\bI'?m hiring\b/i.test(z)||/\bWe are hiring\b/i.test(z);return{name:y,title:$,location:p,currentCompany:D,currentSchool:g,connectionDegree:L,hasVerifiedBadge:wt,photoUrl:bt,connectionsLevel:xe,mutualConnectionsCount:te,followers:je,openToWork:kt,hiring:Ct,about:Te,topSkills:De,availableSections:vt}});return console.log(`Viewed: ${s.name} - ${s.title}`),{...s,profileUrl:t}}catch(o){console.error(`Error viewing profile ${t}:`,o.message);try{await e.goto("about:blank",{timeout:1e4}),await R(1e3)}catch{}return null}}import"dotenv/config";import*as E from"fs";import*as x from"path";import{fileURLToPath as eo}from"url";var to=eo(import.meta.url),oo=x.dirname(x.dirname(to)),B=process.env.DATA_DIR?x.resolve(process.env.DATA_DIR,"market-research"):x.join(oo,"market-research"),no=x.join(B,"scouted-jobs.json"),so=x.join(B,"discovered-people.json"),ze=x.join(B,"visit-log.json"),Ln=x.join(B,"companies.json"),Fn=x.join(B,"job-decisions.json");function ro(){E.existsSync(B)||E.mkdirSync(B,{recursive:!0})}function io(e,t){let o=JSON.stringify(t,null,2),n=e+".tmp";E.writeFileSync(n,o);for(let s=0;s<3;s++)try{E.renameSync(n,e);return}catch(r){if(s<2&&(r.code==="EPERM"||r.code==="EBUSY")){let i=Date.now();for(;Date.now()-i<200;);continue}throw r}}function de(e){try{if(E.existsSync(e))return JSON.parse(E.readFileSync(e,"utf-8"))}catch{}return null}function Qe(){K=null,ao=null,Z=null,co=null,U=null}var K=null;function lo(){return K||(K=de(no)||{},K)}var ao=null;var Z=null;function oe(){return Z||(Z=de(so)||{},Z)}var co=null;var U=null;function Y(){return U||(U=de(ze)||{visits:[]},Array.isArray(U.visits)||(U.visits=[]),U)}function Ke(e,t={}){if(!e)return;ro();let o=Y();o.visits.push({date:new Date().toISOString(),profileUrl:e,name:t.name||null,source:t.source||"manual"});let n=Date.now()-90*24*60*60*1e3;o.visits=o.visits.filter(s=>new Date(s.date).getTime()>n);try{io(ze,o)}catch(s){console.error(`[!] Failed to save visit-log.json: ${s.message}`)}}function Ze(e){let t=Y();for(let o=t.visits.length-1;o>=0;o--)if(t.visits[o].profileUrl===e)return t.visits[o].date;return null}function Xe(){let e=lo(),t=oe(),o=Object.values(e),n=Object.values(t),s={};for(let d of o)d.company&&(s[d.company]=(s[d.company]||0)+1);let r=Object.entries(s).sort((d,y)=>y[1]-d[1]).slice(0,5).map(([d,y])=>`${d} (${y})`),i={};for(let d of o)for(let y of d.techStack||[])i[y]=(i[y]||0)+1;let l=Object.entries(i).sort((d,y)=>y[1]-d[1]).slice(0,8).map(([d,y])=>`${d} (${y})`),c=Y(),a=new Date().toISOString().slice(0,10),m=c.visits.filter(d=>d.date.startsWith(a)).length,w=new Set(c.visits.map(d=>d.profileUrl)).size;return{totalJobs:o.length,totalRecruiters:n.length,topCompanies:r,topTech:l,totalVisited:w,todayVisited:m,lastScoutDate:o.length>0?o.sort((d,y)=>(y.scoutedAt||"").localeCompare(d.scoutedAt||""))[0]?.scoutedAt:null}}async function et(e,t,o={}){let{maxVisits:n=50,delayBetween:s=4e3,skipRecentDays:r=7,verbose:i=!1}=o,l={visited:0,skipped:0,errors:0},c=Date.now()-r*24*60*60*1e3;for(let a of t){if(l.visited>=n)break;if(!a.profileUrl)continue;let m=Ze(a.profileUrl);if(m&&new Date(m).getTime()>c){l.skipped++;continue}try{let w=await Ye(e,a.profileUrl),d=a.source||"visit";w&&ue(a.profileUrl,{...w,source:d}),Ke(a.profileUrl,{name:w?.name||a.name||null,source:d}),l.visited++,i?console.log(` [${l.visited}] ${w?.name||a.profileUrl} \u2014 ${w?.title?.slice(0,40)||"?"}`):l.visited%10===0&&console.log(` Visited: ${l.visited}`);let y=Math.random()*4e3;await R(s+y)}catch(w){l.errors++,i&&console.error(` Error visiting ${a.name||a.profileUrl}: ${w.message?.slice(0,60)}`);let d=(w.message||"").toLowerCase();if(d.includes("detached")||d.includes("session closed")||d.includes("target closed")){console.log(" Fatal browser error \u2014 stopping visits");break}}}return l}function tt(e={}){let{limit:t=150,revisitDays:o=14}=e,n=Y(),s=new Set(n.visits?.map(g=>g.profileUrl)||[]),r=new Map;for(let g of n.visits||[]){let p=r.get(g.profileUrl);(!p||g.date>p)&&r.set(g.profileUrl,g.date)}let i=Date.now()-7*24*60*60*1e3,l=Date.now()-o*24*60*60*1e3;function c(g){let p=r.get(g);return p&&new Date(p).getTime()>i}let a=oe(),m=Object.entries(a).filter(([g])=>!s.has(g)).map(([g,p])=>({profileUrl:g,name:p.name,source:"new-discovered",priority:1,jobCount:p.jobIds?.length||0})),w=Q(),d=Object.entries(w).filter(([g,p])=>p.connectionStatus==="connected"||c(g)?!1:(p.jobCount||p.jobIds?.length||0)>=2).map(([g,p])=>({profileUrl:g,name:p.name,source:"active-recruiter",priority:2,jobCount:p.jobCount||p.jobIds?.length||0})),y=Object.entries(w).filter(([g,p])=>{let L=p.connectionStatus;return!(!L||L==="connected"||L==="pending"||L==="sent_without_note"||L==="sent_with_note"||c(g)||(p.jobCount||p.jobIds?.length||0)>=2)}).map(([g,p])=>({profileUrl:g,name:p.name,source:"unconnected",priority:3,jobCount:p.jobCount||p.jobIds?.length||0})),k=[...r.entries()].filter(([g,p])=>new Date(p).getTime()<l).map(([g,p])=>({profileUrl:g,name:null,source:"revisit",priority:4,lastVisit:p})),$=g=>g.sort(()=>Math.random()-.5);return m.sort((g,p)=>p.jobCount-g.jobCount),d.sort((g,p)=>p.jobCount-g.jobCount),$(y),$(k),[...m,...d,...y,...k].slice(0,t)}function ot(){let e=oe();return Object.entries(e).map(([t,o])=>({profileUrl:t,name:o.name,source:"discovered"}))}function fe(){let e=Q();return Object.entries(e).filter(([,t])=>{let o=t.connectionStatus;return o&&o!=="connected"&&o!=="pending"&&o!=="sent_without_note"&&o!=="sent_with_note"}).map(([t,o])=>({profileUrl:t,name:o.name,source:"unconnected"}))}function me(e=14){let t=Y(),o=Date.now()-e*24*60*60*1e3,n=new Map;for(let s of t.visits){let r=n.get(s.profileUrl);(!r||s.date>r.date)&&n.set(s.profileUrl,s)}return[...n.entries()].filter(([,s])=>new Date(s.date).getTime()<o).map(([s,r])=>({profileUrl:s,name:r.name,source:"revisit"}))}import"dotenv/config";import*as _ from"fs";import*as M from"path";import{fileURLToPath as go}from"url";import"dotenv/config";import{createGraphClient as uo,resolveToken as fo}from"@ilivemylife/graph-sdk";import{config as mo}from"dotenv";mo();var nt=fo()||process.env.TOKEN;nt||(console.error(`
5
5
  [FATAL] No iLiveMyLife token found.`),console.error(" The bot uses Lifebot AI to decide which jobs to apply for"),console.error(` and how to fill application forms. It cannot work without it.
6
6
  `),console.error(" Easiest way:"),console.error(" 1. npm install -g @ilivemylife/graph-sdk"),console.error(` 2. ilml login your@email.com yourpassword
7
7
  `),console.error(` Or add ILML_TOKEN=... to .env
package/ilml-plugin.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "linkedin",
3
- "version": "1.11.2",
3
+ "version": "1.11.4",
4
4
  "description": "Reference ilml plugin: keep a local mirror of your LinkedIn data with AI-assisted workflows",
5
5
  "commands": {
6
6
  "apply": { "run": "node dist/run.mjs" },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ilml-plugin-linkedin",
3
- "version": "1.11.2",
3
+ "version": "1.11.4",
4
4
  "description": "Reference ilml plugin: keep a local mirror of your LinkedIn data and run AI-assisted workflows under your direction.",
5
5
  "type": "module",
6
6
  "files": [