ilml-plugin-linkedin 1.11.4 → 1.11.6
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/CHANGELOG.md +10 -0
- package/dist/applyQueue.mjs +33 -33
- package/dist/daily.mjs +36 -36
- package/dist/enrichProfiles.mjs +31 -31
- package/dist/funnelRunner.mjs +40 -40
- package/dist/messageBot.mjs +69 -69
- package/dist/migrationCli.mjs +32 -32
- package/dist/run.mjs +48 -48
- package/dist/scout.mjs +35 -35
- package/dist/syncAll.mjs +81 -81
- package/dist/viewers.mjs +49 -49
- package/dist/visit.mjs +38 -38
- package/ilml-plugin.json +1 -1
- package/package.json +1 -1
package/dist/syncAll.mjs
CHANGED
|
@@ -1,114 +1,114 @@
|
|
|
1
|
-
var Eo=Object.defineProperty;var Tt=(e,t)=>{for(var o in t)Eo(e,o,{get:t[o],enumerable:!0})};var Ve={jan:0,feb:1,mar:2,apr:3,may:4,jun:5,jul:6,aug:7,sep:8,oct:9,nov:10,dec:11},_o={sunday:0,monday:1,tuesday:2,wednesday:3,thursday:4,friday:5,saturday:6};function be(e){let t=e.match(/(\d{1,2}):(\d{2})\s*(AM|PM)/i);if(!t)return null;let o=parseInt(t[1],10),n=parseInt(t[2],10),s=t[3].toUpperCase()==="PM";return s&&o!==12&&(o+=12),!s&&o===12&&(o=0),{hours:o,minutes:n}}function We(e,t){if(!e)return null;let o=e.trim();if(!o)return null;if(t=t||new Date,/^just\s*now$/i.test(o))return t.toISOString();if(/^today\b/i.test(o)||/^\d{1,2}:\d{2}\s*(AM|PM)$/i.test(o)){let a=be(o),l=new Date(t);return a?l.setHours(a.hours,a.minutes,0,0):l.setHours(0,0,0,0),l.toISOString()}let n=o.match(/^(Sunday|Monday|Tuesday|Wednesday|Thursday|Friday|Saturday)\s*(.*)/i);if(n){let a=_o[n[1].toLowerCase()],l=be(n[2]||""),c=new Date(t),d=c.getDay()-a;return d<=0&&(d+=7),c.setDate(c.getDate()-d),l?c.setHours(l.hours,l.minutes,0,0):c.setHours(0,0,0,0),c.toISOString()}let s=o.match(/^([A-Z][a-z]+)\s+(\d{1,2}),?\s+(\d{4})\s*(.*)/i);if(s){let a=Ve[s[1].toLowerCase().slice(0,3)];if(a===void 0)return null;let l=parseInt(s[2],10),c=parseInt(s[3],10),f=be(s[4]||"");return new Date(c,a,l,f?.hours||0,f?.minutes||0,0,0).toISOString()}let r=o.match(/^([A-Z][a-z]+)\s+(\d{1,2}),\s*(.*)/i);if(r){let a=Ve[r[1].toLowerCase().slice(0,3)];if(a===void 0)return null;let l=parseInt(r[2],10),c=be(r[3]||""),f=t.getFullYear(),d=new Date(f,a,l,c?.hours||0,c?.minutes||0,0,0);return d>t&&d.setFullYear(f-1),d.toISOString()}let i=o.match(/^([A-Z][a-z]+)\s+(\d{1,2})\s*(.*)/i);if(i){let a=Ve[i[1].toLowerCase().slice(0,3)];if(a===void 0)return null;let l=parseInt(i[2],10),c=be(i[3]||""),f=t.getFullYear(),d=new Date(f,a,l,c?.hours||0,c?.minutes||0,0,0);return d>t&&d.setFullYear(f-1),d.toISOString()}return null}async function E(e){let t=Math.floor(Math.random()*2001),o=e+t;return new Promise(n=>setTimeout(n,o))}async function At(e){let t=e.url();if(t.includes("/messaging"))return!1;if(t.includes("/login")||t.includes("/authwall"))throw new Error("Session expired \u2014 redirected to login");console.log(` \u26A0 Off-track: ${t.slice(0,80)}`),console.log(" \u21BB Navigating back to /messaging/..."),await e.goto("https://www.linkedin.com/messaging/",{waitUntil:"domcontentloaded",timeout:6e4}),await E(4e3);let n=e.url();if(n.includes("/login")||n.includes("/authwall"))throw new Error("Session expired during recovery");return console.log(" \u2713 Back on messaging"),!0}function Et(e){return e&&e.replace(/[®™]/g,"").replace(/[\u{1F300}-\u{1F9FF}]|[\u{2600}-\u{27BF}]|[\u{FE00}-\u{FE0F}]|[\u{1F000}-\u{1FAFF}]|[\u{200D}]|[\u{20E3}]|[\u{E0020}-\u{E007F}]/gu,"").replace(/[\u{2B50}\u{2728}\u{2764}\u{270C}\u{270B}\u{1F3FB}-\u{1F3FF}]/gu,"").replace(/\s*\([^)]*[\u4E00-\u9FFF\u3400-\u4DBF][^)]*\)/g,"").replace(/\s{2,}/g," ").trim()}function _t(e){return e&&e.replace(/(?:,\s*(?:Ph\.?D\.?|P\.?E\.?|P\.?Eng\.?|MBA|M\.?Sc\.?|B\.?Sc\.?|M\.?Ed\.?(?:Tech\.?)?|B\.?Tech\.?|B\.?Comm\.?|B\.?Eng\.?|CPA|CFA|CFP|PMP|CISSP|CISM|CSM|CSP|ITIL|FCA|CA|CMA|PE|SE|RA|AIA|LEED\s*AP|SHRM-(?:CP|SCP)|SPHR|PHR|RN|MD|J\.?D\.?|Esq\.?|DDS|OD|DO|PA-C|RD|LPC|LCSW|CDP|ACC|PCC|MCC|ICD\.?D|RCC|CPHR|CHRP|CCP|CIM|CLU|DFSA|CTS|HRMD|CPIR|CPI|MCIPD)\.?\s*)+$/gi,"").replace(/\s+(?:MBA|M\.?Ed\.?(?:Tech\.?)?|Ph\.?D\.?|B\.?Tech\.?|CPHR|CTS)\s*$/gi,"").replace(/,\s*[A-Z][A-Z.\s]*$/g,"").replace(/\s{2,}/g," ").trim()}import{getPluginConfig as Ro}from"@ilivemylife/graph-sdk";var Do=await Ro();for(let[e,t]of Object.entries(Do))process.env[e]===void 0&&(process.env[e]=t);var P=process.env.LINKEDIN_NAME,x=Object.freeze({DRAFT:"draft",CONFIRMED:"confirmed",REJECTED:"rejected",NEEDS_REVIEW:"needs_review",SENT:"sent"}),ve=Object.freeze(new Set([x.DRAFT,x.CONFIRMED,x.NEEDS_REVIEW])),ue=Object.freeze(new Set([x.DRAFT,x.CONFIRMED,x.REJECTED,x.NEEDS_REVIEW]));import"dotenv/config";import*as O from"fs";import*as I from"path";import{fileURLToPath as Oo}from"url";import{execSync as Ye}from"child_process";var Io=Oo(import.meta.url),xo=I.dirname(I.dirname(Io)),U=process.env.DATA_DIR?I.resolve(process.env.DATA_DIR,"collected-profiles"):I.join(xo,"collected-profiles"),Ge=I.join(U,"people.json"),Qs=I.join(U,"jobs.json"),jo=I.join(U,"quota.json"),xe=I.join(U,"conversations.json"),jt=I.join(U,"sync-state.json"),Rt=I.join(U,"application-questions.json"),ze=I.join(U,"profile-history"),Dt=20,Ot=100,fe=null,Mo=null,z=null,oe=null,de=null,te=null;function Mt(){fe=null,Mo=null,z=null,oe=null,de=null,te=null}function Q(){O.existsSync(U)||O.mkdirSync(U,{recursive:!0})}function $e(e,t){let o=JSON.stringify(t,null,2),n=e+".tmp";O.writeFileSync(n,o);for(let s=0;s<3;s++)try{O.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 Ce(e,t){if(!O.existsSync(e))return null;try{let o=JSON.parse(O.readFileSync(e,"utf-8"));try{O.copyFileSync(e,e+".backup")}catch{}return o}catch(o){console.error(`[!] ${t} is corrupted: ${o.message}`),console.error(` File: ${I.resolve(e)}`);let n=e+".backup";if(O.existsSync(n))try{let s=JSON.parse(O.readFileSync(n,"utf-8"));console.error(` Restored from ${n}`);try{O.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{O.copyFileSync(e,e+".corrupted")}catch{}return null}}function Nt(){return new Date().toISOString().split("T")[0]}var No=["name","title","location","company","connectionDegree","connectionsCount","mutualConnections","photoUrl","hasVerifiedBadge","about","currentCompany","currentSchool","connectionsLevel","mutualConnectionsCount","followers","openToWork","hiring","topSkills","availableSections"],Po=["name","title","location","currentCompany","currentSchool","connectionDegree","hasVerifiedBadge","openToWork","hiring","about","topSkills"];function Qe(e){if(!e)return null;let t=e.match(/\/in\/([^/?#]+)/);return t?t[1].toLowerCase():null}function Fo(){O.existsSync(ze)||O.mkdirSync(ze,{recursive:!0})}function Pt(e){let t=Qe(e);return t?I.join(ze,`${t}.json`):null}function Lo(e){let t=Pt(e);return t?Ce(t,`profile-history/${Qe(e)}.json`)||[]:[]}function Uo(e,t){let o=Pt(e);if(!o)return;Fo();let n=Lo(e);n.unshift(t);try{$e(o,n)}catch(s){console.error(`[!] Failed to append profile-history for ${Qe(e)}: ${s.message}`)}}function It(e){return!!(e==null||Array.isArray(e)&&e.length===0||typeof e=="string"&&!e.trim())}function xt(e){return Array.isArray(e)?[...e].map(t=>String(t).trim()).sort().join("||"):typeof e=="string"?e.replace(/\s+/g," ").trim():e}function Bo(e,t){if(!e)return[];let o=[];for(let n of Po){let s=e[n],r=t[n];It(s)||It(r)||xt(s)!==xt(r)&&o.push(n)}return o}function qo(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 V(){return fe||(Q(),fe=Ce(Ge,"people.json")||{},fe)}function je(e){Q(),fe=e;try{$e(Ge,e)}catch(t){console.error(`[!] Failed to save people.json: ${t.message}`),console.error(` Path: ${I.resolve(Ge)}`)}}function ke(){return oe||(Q(),oe=Ce(xe,"conversations.json")||{},Jo(oe),oe)}function Jo(e){let t=0;for(let o of Object.values(e)){if(!(o.draftStatus!==void 0||o.draftPreparedAt!==void 0))continue;let s=(o.messages||[]).find(r=>r.status==="draft");s&&o.draftStatus&&o.draftStatus!=="pending"&&(s.status=o.draftStatus),delete o.draftStatus,delete o.draftPreparedAt,t++}if(t>0){console.log(` [migration] Moved draft state from conv to msg.status for ${t} conversations`);try{$e(xe,e)}catch{}}}function K(e){Q(),oe=e;try{$e(xe,e)}catch(t){console.error(`[!] Failed to save conversations.json: ${t.message}`),console.error(` Path: ${I.resolve(xe)}`)}}function Ft(e,t={}){if(!e)return null;let o=V(),n=Nt(),s=new Date().toISOString(),r=o[e];if(r){let i=Bo(r,t);i.length>0&&(Uo(e,{capturedAt:s,previousLastUpdated:r.lastUpdated||null,changedFields:i,snapshot:qo(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 a of No)t[a]!=null&&(r[a]=t[a]);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 je(o),o[e]}function Ho(){return z||(Q(),z=Ce(jo,"quota.json"),(!z||!z.connections)&&(z={connections:[]}),z)}function Vo(){let e=Ho(),t=new Date;t.setDate(t.getDate()-7);let o=t.toISOString().split("T")[0];return e.connections.filter(n=>n.date>=o)}function Lt(){let e=Vo(),t=Nt(),o=e.filter(s=>s.date===t).length,n=e.length;return{daily:{sent:o,max:Dt,remaining:Dt-o},weekly:{sent:n,max:Ot,remaining:Ot-n}}}function Ke(){return de||(Q(),de=Ce(jt,"sync-state.json")||{},de)}function Ut(e){Q(),de=e;try{$e(jt,e)}catch(t){console.error(`[!] Failed to save sync-state.json: ${t.message}`)}}function Wo(){if(te)return te;Q();try{O.existsSync(Rt)&&(te=JSON.parse(O.readFileSync(Rt,"utf-8")))}catch{}return te||(te={}),te}function Yo(e){return Array.isArray(e.applications)}function Go(){let e=Wo();if(Yo(e))return e.applications;let t=[];for(let o of Object.values(e))o.applications&&t.push(...o.applications);return t}function Bt(){let e=Go(),t=new Map;for(let r of e)for(let i of r.questions||[]){let a=i.question.toLowerCase().trim();if(!a)continue;t.has(a)||t.set(a,{question:i.question,count:0,answers:new Map});let l=t.get(a);l.count++;let c=(i.answer||"").trim();l.answers.set(c,(l.answers.get(c)||0)+1)}let o=[...t.values()].sort((r,i)=>i.count-r.count),n=o.slice(0,15).map(r=>({question:r.question,count:r.count,topAnswer:[...r.answers.entries()].sort((i,a)=>a[1]-i[1])[0]?.[0]||""})),s=o.filter(r=>r.answers.size>1&&r.count>=3).map(r=>({question:r.question,count:r.count,answers:[...r.answers.entries()].sort((i,a)=>a[1]-i[1]).map(([i,a])=>`${i} (${a}x)`)}));return{totalApplications:e.length,uniqueQuestions:t.size,topQuestions:n,inconsistent:s}}function Xe(e){if(O.existsSync(I.join(U,".git")))try{let t={cwd:U,stdio:"pipe",timeout:1e4};Ye("git add conversations.json people.json jobs.json quota.json sync-state.json run-log.json application-questions.json",t);try{Ye("git diff --cached --quiet",t);return}catch{}let o=(e||`sync ${new Date().toISOString().slice(0,16)}`).replace(/"/g,'\\"');Ye(`git commit -m "${o}"`,t)}catch(t){process.env.VERBOSE&&console.log(` [git] snapshot skipped: ${t.message?.slice(0,80)}`)}}import"dotenv/config";import*as se from"fs";import*as F from"path";import{fileURLToPath as zo}from"url";var Qo=zo(import.meta.url),Ko=F.dirname(F.dirname(Qo)),_e=process.env.DATA_DIR?F.resolve(process.env.DATA_DIR,"market-research"):F.join(Ko,"market-research"),Xo=F.join(_e,"scouted-jobs.json"),Zo=F.join(_e,"discovered-people.json"),en=F.join(_e,"visit-log.json"),er=F.join(_e,"companies.json"),tn=F.join(_e,"job-decisions.json");function Me(e){try{if(se.existsSync(e))return JSON.parse(se.readFileSync(e,"utf-8"))}catch{}return null}function qt(){Te=null,Ae=null,Ee=null,nn=null,ne=null}var Te=null;function Ze(){return Te||(Te=Me(Xo)||{},Te)}var Ae=null;function Jt(){return Ae||(Ae=Me(tn)||{},Ae)}var Ee=null;function on(){return Ee||(Ee=Me(Zo)||{},Ee)}var nn=null;var ne=null;function sn(){return ne||(ne=Me(en)||{visits:[]},Array.isArray(ne.visits)||(ne.visits=[]),ne)}function Ht(){let e=Ze(),t=on(),o=Object.values(e),n=Object.values(t),s={};for(let u of o)u.company&&(s[u.company]=(s[u.company]||0)+1);let r=Object.entries(s).sort((u,m)=>m[1]-u[1]).slice(0,5).map(([u,m])=>`${u} (${m})`),i={};for(let u of o)for(let m of u.techStack||[])i[m]=(i[m]||0)+1;let a=Object.entries(i).sort((u,m)=>m[1]-u[1]).slice(0,8).map(([u,m])=>`${u} (${m})`),l=sn(),c=new Date().toISOString().slice(0,10),f=l.visits.filter(u=>u.date.startsWith(c)).length,d=new Set(l.visits.map(u=>u.profileUrl)).size;return{totalJobs:o.length,totalRecruiters:n.length,topCompanies:r,topTech:a,totalVisited:d,todayVisited:f,lastScoutDate:o.length>0?o.sort((u,m)=>(m.scoutedAt||"").localeCompare(u.scoutedAt||""))[0]?.scoutedAt:null}}var rn=new Set(["java","c#",".net","scala","python","typescript","javascript","react","node.js","aws","azure","docker","kubernetes","microservices","graphql","rest","sql","postgresql","mongodb","kafka","redis","ci/cd","terraform","spring"]),an=new Set(["iot","machine learning","ai","data engineering","spark","go","rust","kotlin","next.js","devops","sre"]),Vt=["vancouver","british columbia"],ln=["canada","montreal","quebec","alberta","calgary","ottawa"],cn=["toronto","ontario"],un=["india","pakistan","nigeria","philippines"];function fn(e,t={}){let o=0,n=(e.techStack||[]).map(f=>f.toLowerCase()),s=0,r=0;for(let f of n)rn.has(f)&&s++,an.has(f)&&r++;if(o+=Math.min(s*6,24),o+=Math.min(r*3,6),e.salary){let f=dn(e.salary);f&&(f>=15e4?o+=20:f>=12e4?o+=15:f>=1e5?o+=10:f>=8e4?o+=5:f>=100&&f<1e3?o+=15:f>=70&&f<1e3&&(o+=10))}let i=(e.location||"").toLowerCase(),a=/remote/i.test(i)||e.workplaceType==="remote";un.some(f=>i.includes(f))?o-=30:a&&/united states|usa|\bus\b/i.test(i)?o+=25:a&&Vt.some(f=>i.includes(f))?o+=22:a?o+=20:Vt.some(f=>i.includes(f))?o+=18:ln.some(f=>i.includes(f))?o+=10:cn.some(f=>i.includes(f))&&(o+=5),e.isEasyApply&&(o+=5);let l=(e.seniority||"").toLowerCase();l.includes("senior")||l.includes("lead")||l.includes("staff")?o+=10:l.includes("mid")?o+=5:(l.includes("entry")||l.includes("intern"))&&(o-=10),e.recruiterUrl&&(t.conversationRecruiters?.has(e.recruiterUrl)?o+=20:t.connectedRecruiters?.has(e.recruiterUrl)?o+=15:o+=3);let c=new Set(e.jobTags||[]);return c.has("managerial")?o+=8:c.has("senior-ic")&&(o+=5),c.has("junior")&&(o-=15),c.has("consultant")&&(o-=3),Math.max(0,Math.min(100,o))}function Wt(e=10,t={}){let{easyApplyOnly:o=!0}=t,n=Ze(),s=V(),r=new Set,i=new Set;for(let[d,u]of Object.entries(s))u.connectionStatus==="connected"&&r.add(d);let a={connectedRecruiters:r,conversationRecruiters:i},l=Jt(),c=new Set(["applied","skipped","expired","archived"]);return Object.entries(n).filter(([d,u])=>{if(typeof u!="object"||o&&!u.isEasyApply||u.applied)return!1;let m=l[d];return!(m&&c.has(m.status))}).map(([d,u])=>({jobId:d,score:fn(u,a),job:u})).sort((d,u)=>{if(u.score!==d.score)return u.score-d.score;let m=d.job.scoutedAt||d.job.postedTime||"";return(u.job.scoutedAt||u.job.postedTime||"").localeCompare(m)}).slice(0,e)}function dn(e){if(!e)return null;let t=e.match(/[\$£€CAD\s]*([\d,]+(?:\.\d+)?)\s*[kK]?/g);if(!t||t.length===0)return null;let o=t.map(n=>{let s=parseFloat(n.replace(/[^\d.]/g,""));return n.toLowerCase().includes("k")&&(s*=1e3),s}).filter(n=>n>0);return o.length>=2?(o[0]+o[1])/2:o.length===1?o[0]:null}import hn from"puppeteer";import{addExtra as yn}from"puppeteer-extra";import Sn from"puppeteer-extra-plugin-stealth";import Yt from"fs";import pn from"path";var gn="cookies.json";function mn(){let e=process.env.ILML_SCOPE_DIR;return e?pn.join(e,"plugins-state","linkedin","cookies.json"):gn}function Ne(){let e=mn();if(!Yt.existsSync(e))throw new Error(`Cookies not found at ${e}. Run: ilml linkedin login`);let t;try{t=Yt.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 Qt=yn(hn);Qt.use(Sn());async function Kt(){let e=await Qt.launch({headless:!1,defaultViewport:null,protocolTimeout:3e5}),t=async()=>{console.log(`
|
|
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=
|
|
1
|
+
var Oo=Object.defineProperty;var We=(e,t)=>{for(var o in t)Oo(e,o,{get:t[o],enumerable:!0})};var Ye={jan:0,feb:1,mar:2,apr:3,may:4,jun:5,jul:6,aug:7,sep:8,oct:9,nov:10,dec:11},Io={sunday:0,monday:1,tuesday:2,wednesday:3,thursday:4,friday:5,saturday:6};function ve(e){let t=e.match(/(\d{1,2}):(\d{2})\s*(AM|PM)/i);if(!t)return null;let o=parseInt(t[1],10),n=parseInt(t[2],10),s=t[3].toUpperCase()==="PM";return s&&o!==12&&(o+=12),!s&&o===12&&(o=0),{hours:o,minutes:n}}function Ge(e,t){if(!e)return null;let o=e.trim();if(!o)return null;if(t=t||new Date,/^just\s*now$/i.test(o))return t.toISOString();if(/^today\b/i.test(o)||/^\d{1,2}:\d{2}\s*(AM|PM)$/i.test(o)){let a=ve(o),c=new Date(t);return a?c.setHours(a.hours,a.minutes,0,0):c.setHours(0,0,0,0),c.toISOString()}let n=o.match(/^(Sunday|Monday|Tuesday|Wednesday|Thursday|Friday|Saturday)\s*(.*)/i);if(n){let a=Io[n[1].toLowerCase()],c=ve(n[2]||""),l=new Date(t),d=l.getDay()-a;return d<=0&&(d+=7),l.setDate(l.getDate()-d),c?l.setHours(c.hours,c.minutes,0,0):l.setHours(0,0,0,0),l.toISOString()}let s=o.match(/^([A-Z][a-z]+)\s+(\d{1,2}),?\s+(\d{4})\s*(.*)/i);if(s){let a=Ye[s[1].toLowerCase().slice(0,3)];if(a===void 0)return null;let c=parseInt(s[2],10),l=parseInt(s[3],10),f=ve(s[4]||"");return new Date(l,a,c,f?.hours||0,f?.minutes||0,0,0).toISOString()}let r=o.match(/^([A-Z][a-z]+)\s+(\d{1,2}),\s*(.*)/i);if(r){let a=Ye[r[1].toLowerCase().slice(0,3)];if(a===void 0)return null;let c=parseInt(r[2],10),l=ve(r[3]||""),f=t.getFullYear(),d=new Date(f,a,c,l?.hours||0,l?.minutes||0,0,0);return d>t&&d.setFullYear(f-1),d.toISOString()}let i=o.match(/^([A-Z][a-z]+)\s+(\d{1,2})\s*(.*)/i);if(i){let a=Ye[i[1].toLowerCase().slice(0,3)];if(a===void 0)return null;let c=parseInt(i[2],10),l=ve(i[3]||""),f=t.getFullYear(),d=new Date(f,a,c,l?.hours||0,l?.minutes||0,0,0);return d>t&&d.setFullYear(f-1),d.toISOString()}return null}async function _(e){let t=Math.floor(Math.random()*2001),o=e+t;return new Promise(n=>setTimeout(n,o))}async function Rt(e){let t=e.url();if(t.includes("/messaging"))return!1;if(t.includes("/login")||t.includes("/authwall"))throw new Error("Session expired \u2014 redirected to login");console.log(` \u26A0 Off-track: ${t.slice(0,80)}`),console.log(" \u21BB Navigating back to /messaging/..."),await e.goto("https://www.linkedin.com/messaging/",{waitUntil:"domcontentloaded",timeout:6e4}),await _(4e3);let n=e.url();if(n.includes("/login")||n.includes("/authwall"))throw new Error("Session expired during recovery");return console.log(" \u2713 Back on messaging"),!0}function Dt(e){return e&&e.replace(/[®™]/g,"").replace(/[\u{1F300}-\u{1F9FF}]|[\u{2600}-\u{27BF}]|[\u{FE00}-\u{FE0F}]|[\u{1F000}-\u{1FAFF}]|[\u{200D}]|[\u{20E3}]|[\u{E0020}-\u{E007F}]/gu,"").replace(/[\u{2B50}\u{2728}\u{2764}\u{270C}\u{270B}\u{1F3FB}-\u{1F3FF}]/gu,"").replace(/\s*\([^)]*[\u4E00-\u9FFF\u3400-\u4DBF][^)]*\)/g,"").replace(/\s{2,}/g," ").trim()}function Ot(e){return e&&e.replace(/(?:,\s*(?:Ph\.?D\.?|P\.?E\.?|P\.?Eng\.?|MBA|M\.?Sc\.?|B\.?Sc\.?|M\.?Ed\.?(?:Tech\.?)?|B\.?Tech\.?|B\.?Comm\.?|B\.?Eng\.?|CPA|CFA|CFP|PMP|CISSP|CISM|CSM|CSP|ITIL|FCA|CA|CMA|PE|SE|RA|AIA|LEED\s*AP|SHRM-(?:CP|SCP)|SPHR|PHR|RN|MD|J\.?D\.?|Esq\.?|DDS|OD|DO|PA-C|RD|LPC|LCSW|CDP|ACC|PCC|MCC|ICD\.?D|RCC|CPHR|CHRP|CCP|CIM|CLU|DFSA|CTS|HRMD|CPIR|CPI|MCIPD)\.?\s*)+$/gi,"").replace(/\s+(?:MBA|M\.?Ed\.?(?:Tech\.?)?|Ph\.?D\.?|B\.?Tech\.?|CPHR|CTS)\s*$/gi,"").replace(/,\s*[A-Z][A-Z.\s]*$/g,"").replace(/\s{2,}/g," ").trim()}import{getPluginConfig as xo}from"@ilivemylife/graph-sdk";var jo=await xo();for(let[e,t]of Object.entries(jo))process.env[e]===void 0&&(process.env[e]=t);var P=process.env.LINKEDIN_NAME,j=Object.freeze({DRAFT:"draft",CONFIRMED:"confirmed",REJECTED:"rejected",NEEDS_REVIEW:"needs_review",SENT:"sent"}),$e=Object.freeze(new Set([j.DRAFT,j.CONFIRMED,j.NEEDS_REVIEW])),fe=Object.freeze(new Set([j.DRAFT,j.CONFIRMED,j.REJECTED,j.NEEDS_REVIEW]));import"dotenv/config";import*as O from"fs";import*as x from"path";import{fileURLToPath as No}from"url";import{execSync as ze}from"child_process";var Mo=No(import.meta.url),Po=x.dirname(x.dirname(Mo)),U=process.env.DATA_DIR?x.resolve(process.env.DATA_DIR,"collected-profiles"):x.join(Po,"collected-profiles"),Ke=x.join(U,"people.json"),ir=x.join(U,"jobs.json"),Fo=x.join(U,"quota.json"),je=x.join(U,"conversations.json"),Pt=x.join(U,"sync-state.json"),It=x.join(U,"application-questions.json"),Qe=x.join(U,"profile-history"),xt=20,jt=100,de=null,Lo=null,K=null,ne=null,pe=null,oe=null;function Ft(){de=null,Lo=null,K=null,ne=null,pe=null,oe=null}function Q(){O.existsSync(U)||O.mkdirSync(U,{recursive:!0})}function ke(e,t){let o=JSON.stringify(t,null,2),n=e+".tmp";O.writeFileSync(n,o);for(let s=0;s<3;s++)try{O.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 Ce(e,t){if(!O.existsSync(e))return null;try{let o=JSON.parse(O.readFileSync(e,"utf-8"));try{O.copyFileSync(e,e+".backup")}catch{}return o}catch(o){console.error(`[!] ${t} is corrupted: ${o.message}`),console.error(` File: ${x.resolve(e)}`);let n=e+".backup";if(O.existsSync(n))try{let s=JSON.parse(O.readFileSync(n,"utf-8"));console.error(` Restored from ${n}`);try{O.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{O.copyFileSync(e,e+".corrupted")}catch{}return null}}function Lt(){return new Date().toISOString().split("T")[0]}var Uo=["name","title","location","company","connectionDegree","connectionsCount","mutualConnections","photoUrl","hasVerifiedBadge","about","currentCompany","currentSchool","connectionsLevel","mutualConnectionsCount","followers","openToWork","hiring","topSkills","availableSections"],Bo=["name","title","location","currentCompany","currentSchool","connectionDegree","hasVerifiedBadge","openToWork","hiring","about","topSkills"];function Xe(e){if(!e)return null;let t=e.match(/\/in\/([^/?#]+)/);return t?t[1].toLowerCase():null}function qo(){O.existsSync(Qe)||O.mkdirSync(Qe,{recursive:!0})}function Ut(e){let t=Xe(e);return t?x.join(Qe,`${t}.json`):null}function Jo(e){let t=Ut(e);return t?Ce(t,`profile-history/${Xe(e)}.json`)||[]:[]}function Ho(e,t){let o=Ut(e);if(!o)return;qo();let n=Jo(e);n.unshift(t);try{ke(o,n)}catch(s){console.error(`[!] Failed to append profile-history for ${Xe(e)}: ${s.message}`)}}function Nt(e){return!!(e==null||Array.isArray(e)&&e.length===0||typeof e=="string"&&!e.trim())}function Mt(e){return Array.isArray(e)?[...e].map(t=>String(t).trim()).sort().join("||"):typeof e=="string"?e.replace(/\s+/g," ").trim():e}function Vo(e,t){if(!e)return[];let o=[];for(let n of Bo){let s=e[n],r=t[n];Nt(s)||Nt(r)||Mt(s)!==Mt(r)&&o.push(n)}return o}function Wo(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 W(){return de||(Q(),de=Ce(Ke,"people.json")||{},de)}function Ne(e){Q(),de=e;try{ke(Ke,e)}catch(t){console.error(`[!] Failed to save people.json: ${t.message}`),console.error(` Path: ${x.resolve(Ke)}`)}}function Te(){return ne||(Q(),ne=Ce(je,"conversations.json")||{},Yo(ne),ne)}function Yo(e){let t=0;for(let o of Object.values(e)){if(!(o.draftStatus!==void 0||o.draftPreparedAt!==void 0))continue;let s=(o.messages||[]).find(r=>r.status==="draft");s&&o.draftStatus&&o.draftStatus!=="pending"&&(s.status=o.draftStatus),delete o.draftStatus,delete o.draftPreparedAt,t++}if(t>0){console.log(` [migration] Moved draft state from conv to msg.status for ${t} conversations`);try{ke(je,e)}catch{}}}function X(e){Q(),ne=e;try{ke(je,e)}catch(t){console.error(`[!] Failed to save conversations.json: ${t.message}`),console.error(` Path: ${x.resolve(je)}`)}}function Bt(e,t={}){if(!e)return null;let o=W(),n=Lt(),s=new Date().toISOString(),r=o[e];if(r){let i=Vo(r,t);i.length>0&&(Ho(e,{capturedAt:s,previousLastUpdated:r.lastUpdated||null,changedFields:i,snapshot:Wo(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 a of Uo)t[a]!=null&&(r[a]=t[a]);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 Ne(o),o[e]}function Go(){return K||(Q(),K=Ce(Fo,"quota.json"),(!K||!K.connections)&&(K={connections:[]}),K)}function zo(){let e=Go(),t=new Date;t.setDate(t.getDate()-7);let o=t.toISOString().split("T")[0];return e.connections.filter(n=>n.date>=o)}function qt(){let e=zo(),t=Lt(),o=e.filter(s=>s.date===t).length,n=e.length;return{daily:{sent:o,max:xt,remaining:xt-o},weekly:{sent:n,max:jt,remaining:jt-n}}}function Ze(){return pe||(Q(),pe=Ce(Pt,"sync-state.json")||{},pe)}function Jt(e){Q(),pe=e;try{ke(Pt,e)}catch(t){console.error(`[!] Failed to save sync-state.json: ${t.message}`)}}function Ko(){if(oe)return oe;Q();try{O.existsSync(It)&&(oe=JSON.parse(O.readFileSync(It,"utf-8")))}catch{}return oe||(oe={}),oe}function Qo(e){return Array.isArray(e.applications)}function Xo(){let e=Ko();if(Qo(e))return e.applications;let t=[];for(let o of Object.values(e))o.applications&&t.push(...o.applications);return t}function Ht(){let e=Xo(),t=new Map;for(let r of e)for(let i of r.questions||[]){let a=i.question.toLowerCase().trim();if(!a)continue;t.has(a)||t.set(a,{question:i.question,count:0,answers:new Map});let c=t.get(a);c.count++;let l=(i.answer||"").trim();c.answers.set(l,(c.answers.get(l)||0)+1)}let o=[...t.values()].sort((r,i)=>i.count-r.count),n=o.slice(0,15).map(r=>({question:r.question,count:r.count,topAnswer:[...r.answers.entries()].sort((i,a)=>a[1]-i[1])[0]?.[0]||""})),s=o.filter(r=>r.answers.size>1&&r.count>=3).map(r=>({question:r.question,count:r.count,answers:[...r.answers.entries()].sort((i,a)=>a[1]-i[1]).map(([i,a])=>`${i} (${a}x)`)}));return{totalApplications:e.length,uniqueQuestions:t.size,topQuestions:n,inconsistent:s}}function et(e){if(O.existsSync(x.join(U,".git")))try{let t={cwd:U,stdio:"pipe",timeout:1e4};ze("git add conversations.json people.json jobs.json quota.json sync-state.json run-log.json application-questions.json",t);try{ze("git diff --cached --quiet",t);return}catch{}let o=(e||`sync ${new Date().toISOString().slice(0,16)}`).replace(/"/g,'\\"');ze(`git commit -m "${o}"`,t)}catch(t){process.env.VERBOSE&&console.log(` [git] snapshot skipped: ${t.message?.slice(0,80)}`)}}import"dotenv/config";import*as re from"fs";import*as F from"path";import{fileURLToPath as Zo}from"url";var en=Zo(import.meta.url),tn=F.dirname(F.dirname(en)),Re=process.env.DATA_DIR?F.resolve(process.env.DATA_DIR,"market-research"):F.join(tn,"market-research"),on=F.join(Re,"scouted-jobs.json"),nn=F.join(Re,"discovered-people.json"),sn=F.join(Re,"visit-log.json"),ur=F.join(Re,"companies.json"),rn=F.join(Re,"job-decisions.json");function Me(e){try{if(re.existsSync(e))return JSON.parse(re.readFileSync(e,"utf-8"))}catch{}return null}function Vt(){Ee=null,_e=null,Ae=null,cn=null,se=null}var Ee=null;function tt(){return Ee||(Ee=Me(on)||{},Ee)}var _e=null;function Wt(){return _e||(_e=Me(rn)||{},_e)}var Ae=null;function an(){return Ae||(Ae=Me(nn)||{},Ae)}var cn=null;var se=null;function ln(){return se||(se=Me(sn)||{visits:[]},Array.isArray(se.visits)||(se.visits=[]),se)}function Yt(){let e=tt(),t=an(),o=Object.values(e),n=Object.values(t),s={};for(let u of o)u.company&&(s[u.company]=(s[u.company]||0)+1);let r=Object.entries(s).sort((u,m)=>m[1]-u[1]).slice(0,5).map(([u,m])=>`${u} (${m})`),i={};for(let u of o)for(let m of u.techStack||[])i[m]=(i[m]||0)+1;let a=Object.entries(i).sort((u,m)=>m[1]-u[1]).slice(0,8).map(([u,m])=>`${u} (${m})`),c=ln(),l=new Date().toISOString().slice(0,10),f=c.visits.filter(u=>u.date.startsWith(l)).length,d=new Set(c.visits.map(u=>u.profileUrl)).size;return{totalJobs:o.length,totalRecruiters:n.length,topCompanies:r,topTech:a,totalVisited:d,todayVisited:f,lastScoutDate:o.length>0?o.sort((u,m)=>(m.scoutedAt||"").localeCompare(u.scoutedAt||""))[0]?.scoutedAt:null}}var un=new Set(["java","c#",".net","scala","python","typescript","javascript","react","node.js","aws","azure","docker","kubernetes","microservices","graphql","rest","sql","postgresql","mongodb","kafka","redis","ci/cd","terraform","spring"]),fn=new Set(["iot","machine learning","ai","data engineering","spark","go","rust","kotlin","next.js","devops","sre"]),Gt=["vancouver","british columbia"],dn=["canada","montreal","quebec","alberta","calgary","ottawa"],pn=["toronto","ontario"],gn=["india","pakistan","nigeria","philippines"];function mn(e,t={}){let o=0,n=(e.techStack||[]).map(f=>f.toLowerCase()),s=0,r=0;for(let f of n)un.has(f)&&s++,fn.has(f)&&r++;if(o+=Math.min(s*6,24),o+=Math.min(r*3,6),e.salary){let f=hn(e.salary);f&&(f>=15e4?o+=20:f>=12e4?o+=15:f>=1e5?o+=10:f>=8e4?o+=5:f>=100&&f<1e3?o+=15:f>=70&&f<1e3&&(o+=10))}let i=(e.location||"").toLowerCase(),a=/remote/i.test(i)||e.workplaceType==="remote";gn.some(f=>i.includes(f))?o-=30:a&&/united states|usa|\bus\b/i.test(i)?o+=25:a&&Gt.some(f=>i.includes(f))?o+=22:a?o+=20:Gt.some(f=>i.includes(f))?o+=18:dn.some(f=>i.includes(f))?o+=10:pn.some(f=>i.includes(f))&&(o+=5),e.isEasyApply&&(o+=5);let c=(e.seniority||"").toLowerCase();c.includes("senior")||c.includes("lead")||c.includes("staff")?o+=10:c.includes("mid")?o+=5:(c.includes("entry")||c.includes("intern"))&&(o-=10),e.recruiterUrl&&(t.conversationRecruiters?.has(e.recruiterUrl)?o+=20:t.connectedRecruiters?.has(e.recruiterUrl)?o+=15:o+=3);let l=new Set(e.jobTags||[]);return l.has("managerial")?o+=8:l.has("senior-ic")&&(o+=5),l.has("junior")&&(o-=15),l.has("consultant")&&(o-=3),Math.max(0,Math.min(100,o))}function zt(e=10,t={}){let{easyApplyOnly:o=!0}=t,n=tt(),s=W(),r=new Set,i=new Set;for(let[d,u]of Object.entries(s))u.connectionStatus==="connected"&&r.add(d);let a={connectedRecruiters:r,conversationRecruiters:i},c=Wt(),l=new Set(["applied","skipped","expired","archived"]);return Object.entries(n).filter(([d,u])=>{if(typeof u!="object"||o&&!u.isEasyApply||u.applied)return!1;let m=c[d];return!(m&&l.has(m.status))}).map(([d,u])=>({jobId:d,score:mn(u,a),job:u})).sort((d,u)=>{if(u.score!==d.score)return u.score-d.score;let m=d.job.scoutedAt||d.job.postedTime||"";return(u.job.scoutedAt||u.job.postedTime||"").localeCompare(m)}).slice(0,e)}function hn(e){if(!e)return null;let t=e.match(/[\$£€CAD\s]*([\d,]+(?:\.\d+)?)\s*[kK]?/g);if(!t||t.length===0)return null;let o=t.map(n=>{let s=parseFloat(n.replace(/[^\d.]/g,""));return n.toLowerCase().includes("k")&&(s*=1e3),s}).filter(n=>n>0);return o.length>=2?(o[0]+o[1])/2:o.length===1?o[0]:null}import bn from"puppeteer";import{addExtra as vn}from"puppeteer-extra";import $n from"puppeteer-extra-plugin-stealth";import Kt from"fs";import yn from"path";var Sn="cookies.json";function wn(){let e=process.env.ILML_SCOPE_DIR;return e?yn.join(e,"plugins-state","linkedin","cookies.json"):Sn}function Pe(){let e=wn();if(!Kt.existsSync(e))throw new Error(`Cookies not found at ${e}. Run: ilml linkedin login`);let t;try{t=Kt.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 Zt=vn(bn);Zt.use($n());async function eo(){let e=await Zt.launch({headless:!1,defaultViewport:null,protocolTimeout:3e5}),t=async()=>{console.log(`
|
|
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=Pe();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}}async function et(e){await e.goto("https://www.linkedin.com/messaging/",{waitUntil:"domcontentloaded",timeout:6e4}),await E(4e3);let t=e.url();if(t.includes("/login")||t.includes("/authwall"))throw new Error("Redirected to login \u2014 cookies expired. Run: npm run login");try{await e.waitForSelector(".msg-conversations-container__conversations-list, .msg-conversation-listitem",{timeout:15e3})}catch{console.warn(" Inbox container not found via known selectors, proceeding..."),await E(3e3)}console.log(" Inbox loaded.")}async function Xt(e){let t=await e.evaluate(()=>{let o=document.querySelectorAll(".msg-conversation-listitem"),n=[];for(let s of o){let i=s.querySelector(".msg-conversation-listitem__participant-names, .msg-conversation-card__participant-names")?.textContent?.trim()?.replace(/\s+/g," ")||null;if(!i)continue;let l=s.querySelector(".msg-conversation-card__message-snippet")?.textContent?.trim()?.replace(/\s+/g," ")||"";if(/^\s*Sponsored\b/i.test(l)||/^\s*Promoted\b/i.test(l))continue;let f=s.querySelector(".msg-conversation-listitem__time-stamp, .msg-conversation-card__time-stamp")?.textContent?.trim()||"",d=s.querySelector(".msg-conversation-card__convo-item-container--unread")!==null||s.querySelector(".msg-conversation-card__unread-count")!==null,u,m;if(l.startsWith("You:"))u="me",m=l.slice(4).trim();else{let y=i.split(" ")[0];if(l.startsWith(i+":")||l.startsWith(y+":")){u="them";let v=l.indexOf(":");m=l.slice(v+1).trim()}else u="them",m=l}let S=s.getBoundingClientRect();n.push({name:i,lastMessagePreview:m,lastMessageTime:f,unread:d,lastMessageBy:u,x:S.x+S.width/2,y:S.y+S.height/2})}return n});for(let o of t)o.lastMessageTime=We(o.lastMessageTime)||o.lastMessageTime;return t}async function tt(e){return e.evaluate(()=>{let t=[".msg-conversations-container__conversations-list",".msg-conversations-container",'[class*="msg-conversations-container"] ul'];for(let n of t){let s=document.querySelector(n);if(s&&s.scrollHeight>s.clientHeight)return s.scrollBy(0,600),{scrollTop:s.scrollTop,scrollHeight:s.scrollHeight}}let o=document.querySelector(".msg-conversation-listitem");if(o){let n=o.parentElement;for(;n;){if(n.scrollHeight>n.clientHeight+10)return n.scrollBy(0,600),{scrollTop:n.scrollTop,scrollHeight:n.scrollHeight};n=n.parentElement}}return null})}async function wn(e){return e.evaluate(()=>{let t=/^status\s+is\s+|^online$|^offline$|^away$|^busy$|^active\s/i,o=['.msg-overlay-conversation-bubble__header a[href*="/in/"] .truncate','.msg-overlay-conversation-bubble__header a[href*="/in/"]','.msg-overlay-bubble-header__title a[href*="/in/"]',".msg-overlay-bubble-header__title",".msg-overlay-conversation-bubble__header .truncate",".msg-thread__link-to-profile","h2.msg-entity-lockup__entity-title",".msg-s-message-list-container h2",".msg-thread h2",".msg-conversations-container__title-row h2",'.msg-thread a[href*="/in/"]'];for(let s of o){let r=document.querySelector(s);if(r){let i=r.textContent?.trim()?.replace(/\s+/g," ");if(i&&i.length>1&&i.length<100&&!t.test(i))return i}}let n=document.querySelectorAll(".msg-s-message-list-container h2, .msg-thread h2");for(let s of n){let r=s.textContent?.trim();if(r&&r.length>1&&r.length<100&&!/conversation/i.test(r)&&!t.test(r))return r}return null})}async function ot(e){return e.evaluate(()=>{let t=[".msg-thread__link-to-profile",'.msg-entity-lockup__entity-title a[href*="/in/"]','.msg-s-message-list-container a[href*="/in/"]','.msg-thread a[href*="/in/"]','.msg-conversations-container__title-row a[href*="/in/"]','.msg-overlay-conversation-bubble__header a[href*="/in/"]','.msg-overlay-bubble-header a[href*="/in/"]','.msg-overlay-conversation-bubble a[href*="/in/"]'];for(let o of t){let n=document.querySelector(o);if(n){let s=n.getAttribute("href")||"";if(s&&s.includes("/in/"))return s.startsWith("/")&&(s="https://www.linkedin.com"+s),s=s.replace(/\/$/,"").split("?")[0],s}}return null})}async function Gt(e,t){let o=await wn(e);if(!o)return{match:!1,headerName:null};let n=t.split(" ")[0].toLowerCase(),s=t.toLowerCase().split(/[\s,]+/).filter(a=>a.length>1),r=o.toLowerCase();return{match:r.includes(n)||s.some(a=>r.includes(a)),headerName:o}}async function Pe(e,t){let o=t.split(" ")[0],n=8e3,s=500,r=0;for(;r<n;){let{match:l,headerName:c}=await Gt(e,t);if(l)return await E(500),{verified:!0,headerName:c};let f=await e.evaluate(()=>{let d=document.querySelectorAll(".msg-s-message-group__name"),u=Array.from(d).map(S=>S.textContent?.trim()).filter(Boolean),m=document.querySelectorAll(".msg-s-event-listitem").length;return{allSenders:u,msgCount:m}});if(f.msgCount>0&&f.allSenders.some(u=>u.toLowerCase().includes(o.toLowerCase())))return await E(300),{verified:!0,headerName:o};await E(s),r+=s}let{match:i,headerName:a}=await Gt(e,t);return i?{verified:!0,headerName:a}:{verified:!1,headerName:a}}async function bn(e){let o=0,n=0;for(let s=0;s<50;s++){let r=await e.evaluate(()=>{let i=document.querySelector(".msg-thread .msg-s-message-list-container")||document.querySelector(".msg-s-message-list-container");if(!i){let c=document.querySelector(".msg-overlay-conversation-bubble");c&&(i=c.querySelector(".msg-s-message-list-container")||c.querySelector(".msg-s-message-list"))}if(!i)return{found:!1};let a=i.querySelectorAll("li.msg-s-message-list__event").length;return i.scrollTop<=1?{found:!0,atTop:!0,msgCount:a}:(i.scrollTop=0,{found:!0,atTop:!1,msgCount:a})});if(!r.found||r.atTop)break;if(await E(800),r.msgCount===o){if(n++,n>=3)break}else n=0;o=r.msgCount}}async function nt(e,{scrollUp:t=!0}={}){t&&await bn(e);let o=await e.evaluate(()=>{let n=[],s="",r=document.querySelector(".msg-thread .msg-s-message-list-container")||document.querySelector(".msg-s-message-list-container");if(!r){let f=document.querySelector(".msg-overlay-conversation-bubble");f&&(r=f.querySelector(".msg-s-message-list-container")||f.querySelector(".msg-s-message-list"))}if(!r)return n;let i=r.querySelectorAll("li.msg-s-message-list__event");if(i.length===0){let f=document.querySelector(".msg-overlay-conversation-bubble, .msg-overlay-list-bubble");f&&(i=f.querySelectorAll("li.msg-s-message-list__event, .msg-s-event-listitem"))}function a(f){let d=f.getAttribute("data-event-urn");if(!d)return null;let u=d.match(/,2-([A-Za-z0-9+/=]+)\)/);if(!u)return null;try{let S=atob(u[1]).match(/^(\d{13})/);if(!S)return null;let y=Number(S[1]);if(y>14200704e5&&y<20512224e5)return y}catch{}return null}let l="";function c(f,d,u){let m=a(f),y=f.querySelector(".msg-s-message-group__timestamp, time")?.textContent?.trim()||"",v=f.querySelector(".msg-s-event-listitem__message-bubble");if(!v)return null;let $=v.textContent?.trim()?.replace(/Open Emoji Keyboard|Reply to this message|React with[^.]+/g,"")?.replace(/^(?:👏\s*👍\s*😊\s*)+/,"")?.replace(/\s+/g," ")?.trim()||"";if(!$)return null;let C=m?new Date(m).toISOString():u?`${u} ${y}`.trim():y;return{sender:d,text:$,time:C}}for(let f of i){let d=f.querySelector("time.msg-s-message-list__time-heading");d&&(l=d.textContent?.trim()||l);let u=f.querySelectorAll(".msg-s-message-group");if(u.length>0)for(let m of u){let S=m.querySelector(".msg-s-message-group__name");S&&(s=S.textContent?.trim()||s);let y=s||"Unknown",v=m.querySelectorAll(".msg-s-event-listitem");for(let $ of v){let C=c($,y,l);C&&n.push(C)}}else{let m=f.querySelectorAll(".msg-s-event-listitem");for(let S of m){let y=S.querySelector(".msg-s-message-group__name");y&&(s=y.textContent?.trim()||s);let $=c(S,s||"Unknown",l);$&&n.push($)}}}if(n.length===0){let f=document.querySelectorAll(".msg-s-event-listitem--group-a11y-heading");for(let d of f){let u=d.textContent?.trim();u&&n.push({sender:"",text:u,time:""})}}return n});for(let n of o)(!n.time||!/^\d{4}-\d{2}-\d{2}T/.test(n.time))&&(n.time=We(n.time)||n.time);return o}function st(e,t,o){if(e.length===0)return!0;let n=t.split(" ")[0].toLowerCase(),s=t.toLowerCase().split(/[\s,]+/).filter(c=>c.length>1),r=[...new Set(e.map(c=>c.sender).filter(c=>c&&c!=="Unknown"))],i=r.some(c=>{let f=c.toLowerCase();return f.includes(n)||s.some(d=>f.includes(d))}),a=P.toLowerCase().split(/\s+/),l=r.every(c=>{let f=c.toLowerCase();return a.some(d=>f.includes(d))});if(i)return!0;if(l){for(let c of e){let d=(c.text||"").trim().match(/^(?:Hi|Hey|Hello|Dear)\s+([A-Z][a-z]+)/);if(d){let u=d[1].toLowerCase();if(u!=="there"&&u!=="all"&&!(s.some(S=>S===u)||u===n))return console.log(` STALE DATA: message says "Hi ${d[1]}" but expected "${t}"`),!1}}return!0}if(o?.length>0){let c=o.map(d=>d.toLowerCase());if(r.some(d=>{let u=d.toLowerCase();return c.some(m=>u.includes(m.split(" ")[0]))}))return console.log(` STALE DATA: messages belong to previous conversation (senders: ${r.join(", ")})`),!1}return console.log(` Sender warning: no match for "${t}" (found: ${r.join(", ")})`),!1}function pe(e){return(e||"").replace(/\s+/g," ").trim().toLowerCase()}function rt(e,t){if(!e||e.length===0)return t||[];if(!t||t.length===0)return e;let o=u=>pe(u.sender)+"||"+pe(u.text),n=new Set(t.map(o)),s=new Set(e.map(o)),r=e.filter(u=>ue.has(u.status)?!1:!n.has(o(u))),i=t.filter(u=>!s.has(o(u))),a=t.filter(u=>s.has(o(u))),l=e.filter(u=>ue.has(u.status)),c=[...r,...a,...i],f=[];for(let u of c){let m=pe(u.text),S=pe(u.sender),y=!1;for(let v=0;v<f.length;v++){let $=f[v];if(pe($.sender)!==S)continue;let C=pe($.text);if(m.length>C.length&&m.endsWith(C)){y=!0;break}if(C.length>m.length&&C.endsWith(m)){f[v]=u,y=!0;break}}y||f.push(u)}return[...f,...l]}async function Zt(e,t){await et(e),await E(2500),await e.evaluate(()=>{let a=[".msg-conversations-container__conversations-list",".msg-conversations-container"];for(let l of a){let c=document.querySelector(l);if(c){c.scrollTop=0;return}}}),await E(500);async function o(){return e.evaluate(a=>{let l=document.querySelectorAll(".msg-conversation-listitem");for(let c of l)if(c.querySelector(".msg-conversation-listitem__participant-names")?.textContent?.trim()?.replace(/\s+/g," ")===a){let u=c.getBoundingClientRect();if(u.height>0)return{x:u.x+u.width/2,y:u.y+u.height/2}}return null},t)}let n=await o(),s=50,r=-1,i=0;for(let a=0;a<s&&!n;a++){let l=await tt(e);if(!l)break;if(l.scrollTop===r){if(i++,i>=2)break}else i=0,r=l.scrollTop;await E(600),n=await o()}return n?(await e.mouse.click(n.x,n.y),await E(3e3),Pe(e,t)):{verified:!1,headerName:null}}async function it(e,t){let o=await e.evaluate(i=>{let a=document.querySelectorAll(".msg-conversation-listitem");for(let l of a)if(l.querySelector(".msg-conversation-listitem__participant-names")?.textContent?.trim()?.replace(/\s+/g," ")===i){let d=l.getBoundingClientRect();if(d.height>0)return{x:d.x+d.width/2,y:d.y+d.height/2}}return null},t);if(o){await e.mouse.click(o.x,o.y),await E(3e3);let i=await Pe(e,t);if(i.verified)return i}let n=Et(t),s=n!==t?[t,n]:[t];for(let i of s)if(await zt(e,i,t)){let l=await Pe(e,t);if(l.verified)return l}let r=_t(n);if(r!==n&&r.length>0&&await zt(e,r,t,{requireUnique:!0})){let a=await Pe(e,t);if(a.verified)return a}return{verified:!1,headerName:null}}async function zt(e,t,o,n={}){let{requireUnique:s=!1}=n,r=await e.$("input#search-conversations, .msg-cross-pillar-search-form__search-field");if(!r)return!1;await r.click(),await E(300),await e.keyboard.down("Control"),await e.keyboard.press("a"),await e.keyboard.up("Control"),await e.keyboard.press("Backspace"),await E(300),await r.type(t,{delay:30}),await E(500),await e.keyboard.press("Enter"),await E(3500);let i=await e.evaluate((a,l)=>{let c=a.split(" ")[0].toLowerCase(),f=a.toLowerCase(),d=document.querySelectorAll(".msg-conversation-listitem"),u=[],m=[];for(let y of d){let $=y.querySelector(".msg-conversation-listitem__participant-names, .msg-conversation-card__participant-names")?.textContent?.trim()?.replace(/\s+/g," ")||"",C=$.toLowerCase();if(C.includes(c)){let g=y.getBoundingClientRect();if(g.height>0){let p=$.includes(",")||/\d+ other/.test($),w=C===f?3:C.includes(f)?2:1,k={text:$,isGroup:p,score:w,x:g.x+g.width/2,y:g.y+g.height/2};p?m.push(k):u.push(k)}}}u.sort((y,v)=>v.score-y.score);let S=[...u,...m];return l&&S.length!==1?null:u[0]||m[0]||null},o,s);return i?(s&&console.log(" (found via stripped designations)"),await e.mouse.click(i.x,i.y),!0):!1}async function X(e){let t=await e.$("input#search-conversations, .msg-cross-pillar-search-form__search-field");t&&(await t.click(),await E(200),await e.keyboard.down("Control"),await e.keyboard.press("a"),await e.keyboard.up("Control"),await e.keyboard.press("Backspace"),await e.keyboard.press("Escape"),await E(1e3))}function lt(e,t,o){if(!e)return e;let s=(e.messages||[]).filter(l=>!ue.has(l.status)),r=Rn(t,e.profileUrl,o),i=Cn(s,t);if(e._cardSynced||(e.lastMessageBy=i),e.conversationStatus=e.lastMessageBy==="me"?"awaiting_reply":"unanswered_by_us",e.categorySource!=="manual"){let l=vn(s,t,r);l&&(e.category=l,e.categorySource=e.categorySource||"message_content")}if(e.situation=$n(s,i,t),s.length>0){let l=s[s.length-1];e.lastMessageTime=l.time}if(s.length>0&&!e.initiatedBy){let l=s[0];l.sender===P?e.initiatedBy="us":l.sender!=="Unknown"&&l.sender!==""&&(e.initiatedBy="them")}e.engagement=no(s,t),e.personContext=En(r),e.summary=kn(e,t,r),e.autoTags=Tn(e,t,r);let a=An(e);return e.actionNeeded=a.action,e.actionDetail=a.detail,e.suggestedPriority=_n(e),e}var to={recruiter:{theirMessages:[/\b(recruiter|recruiting|talent.?acqui|staffing|headhunter|head\s*hunter|sourcer|executive\s*search)\b/i],title:[/\b(recruiter|recruiting|talent.?acqui|staffing|sourcer|sourcing)\b/i,/\b(head\s*hunter|executive\s*search)\b/i]},hiring_manager:{theirMessages:[/\b(my|our)\s+team\b/i,/\b(i.?m|we.?re|i\s+am|we\s+are)\s+(currently\s+|actively\s+|now\s+)?(looking|hiring|recruiting)\b/i,/\bjoin\s+(my|our)\b/i,/\bdirect.?report/i,/\b(engineering manager|team lead|vp of|director of|head of)\b/i],title:[/\b(engineering manager|team lead|vp|director|head of engineering)\b/i]},founder:{theirMessages:[/\b(founder|co-founder|ceo|cto|my company|my startup|i.?m building)\b/i],title:[/\b(founder|co-founder|ceo|cto)\b/i]},investor:{theirMessages:[/\b(investor|invest|funding|vc|venture|angel)\b/i],title:[/\b(investor|venture|capital|angel|partner at)\b/i,/\binvestment\s+(manager|partner|director|associate|analyst|principal)\b/i]},coach:{theirMessages:[/\b(coach|coaching|mentor|mentoring|career.?(advice|guidance|help))\b/i,/\b(resume.?(review|writing)|interview.?prep|career.?transition)\b/i],title:[/\b(coach|mentor|career.?(consultant|advisor))\b/i]},peer:{theirMessages:[/\b(fellow|peer|networking|connect|nice.?to.?meet|great.?to.?connect)\b/i,/\b(your (profile|background|experience).*interest)\b/i]},sales:{theirMessages:[/\b(sales|business develop|partnership|proposal|offer|demo|pricing|platform|solution)\b/i],title:[/\b(sales|business develop|account.?exec|bdm|bdr|sdr)\b/i]},spam:{theirMessages:[/\b(crypto|bitcoin|forex|trading.?signal|guaranteed.?return|make.?money)\b/i,/\b(whatsapp|telegram.?group|click (here|this link))\b/i]},event:{theirMessages:[/\b(event|conference|webinar|summit|meetup|workshop|speak(er|ing))\b/i,/\b(attend|register|rsvp|invitation|invite.?you)\b/i]}};function vn(e,t,o){if(!e||e.length===0)return o?.title?at(o.title):null;let n=e.filter(s=>s.sender!==P&&s.sender!=="Unknown"&&s.sender!=="").map(s=>s.text).join(" ");for(let[s,r]of Object.entries(to))if(r.theirMessages){for(let i of r.theirMessages)if(i.test(n))return s}if(o?.title){let s=at(o.title);if(s)return s}return null}function at(e){if(!e)return null;for(let[t,o]of Object.entries(to))if(o.title){for(let n of o.title)if(n.test(e))return t}return null}var eo=/\b(call|meet|teams|zoom|schedule|available|availability|invite|slot|calendar)\b/i;function $n(e,t,o){if(!e||e.length===0)return"no_messages";let n=e[e.length-1];if(e.length===1)return t==="them"?"initial_outreach_unanswered":"we_initiated_waiting";if(t==="me")return eo.test(n.text)?"scheduling_proposed_by_us":e.length>=6?"active_conversation_ball_in_their_court":"awaiting_their_reply";let s=[...e].reverse().find(r=>r.sender!==P&&r.sender!=="Unknown");return eo.test(s?.text||"")?"they_want_to_schedule_call":e.length>=6?"active_conversation_needs_our_reply":"needs_our_reply"}function Cn(e,t){if(!e||e.length===0)return"them";for(let o=e.length-1;o>=0;o--){let n=e[o].sender;if(n===P)return"me";if(n!=="Unknown"&&n!=="")return"them"}return"them"}function kn(e,t,o){let n=e.messages||[];if(n.length===0)return null;let s=[],r=e.category,i=o?.title,a=o?.company,l=o?.location;r==="recruiter"&&a?s.push(`Recruiter from ${a}`):r==="hiring_manager"&&a?s.push(`Hiring manager at ${a}`):r==="founder"&&a?s.push(`Founder of ${a}`):r&&i?s.push(`${ct(r)}: ${ge(i,40)}`):i?s.push(ge(i,50)):s.push(t.split(" ")[0]);let c=n.filter(S=>S.sender!==P&&S.sender!=="Unknown").map(S=>S.text).join(" "),f=Dn(c),d=On(c),u=In(c),m=[];return f&&m.push(f),u&&u!==l&&m.push(u),d&&m.push(d),m.length>0&&s.push(m.join(", ")),e.situation==="they_want_to_schedule_call"?s.push("wants to schedule call"):e.situation==="scheduling_proposed_by_us"?s.push("we proposed scheduling"):e.conversationStatus==="unanswered_by_us"?s.push("needs reply"):e.conversationStatus==="awaiting_reply"&&s.push("waiting on them"),s.join(". ").replace(/\.\./g,".")}function Tn(e,t,o){let n=[],s=(e.messages||[]).filter(c=>!ue.has(c.status)),r=s.map(c=>c.text).join(" "),i=s.filter(c=>c.sender!==P&&c.sender!=="Unknown").map(c=>c.text).join(" "),a=oo(i,o?.location);n.push(...a),/\b(contract|contractor|c2c|corp.?to.?corp)\b/i.test(r)&&n.push("contract"),/\b(full.?time|permanent|fte|perm)\b/i.test(r)&&n.push("full-time"),/\b(remote|work.?from.?home|wfh)\b/i.test(r)&&n.push("remote"),/\b(hybrid)\b/i.test(r)&&n.push("hybrid"),/\b(on.?site|in.?office|in.?person)\b/i.test(r)&&n.push("onsite"),/\b(java)\b/i.test(r)&&!/javascript/i.test(r)&&n.push("java"),/\b(python)\b/i.test(r)&&n.push("python"),/\b(react|angular|vue|front.?end)\b/i.test(r)&&n.push("frontend"),/\b(node|backend|back.?end|api|microservice)\b/i.test(r)&&n.push("backend"),/\b(full.?stack)\b/i.test(r)&&n.push("fullstack"),/\b(devops|sre|cloud|aws|azure|gcp|kubernetes|k8s)\b/i.test(r)&&n.push("devops"),/\b(data.?engineer|data.?scien|ml|machine.?learn|ai)\b/i.test(r)&&n.push("data/ml"),/\b(lead|senior|staff|principal|architect)\b/i.test(r)&&n.push("senior-role"),/\b(manager|director|vp|head of)\b/i.test(r)&&n.push("management"),/\$[\d,]+|\d+k|\d+\/hr|\d+\s*per\s*(hour|annum|year)/i.test(r)&&n.push("has-salary");let l=e.engagement||no(s,t);return l.messageCount>=6&&n.push("active-thread"),l.theirCount===0&&l.ourCount>0&&n.push("no-response"),l.ourCount===0&&l.theirCount>0&&n.push("unanswered"),e.situation==="they_want_to_schedule_call"&&n.push("hot-lead"),(e.messages||[]).some(c=>ve.has(c.status))&&n.push("has-draft"),[...new Set(n)]}function oo(e,t){let o=[],n=["toronto","vancouver","montreal","ottawa","calgary","edmonton","new york","san francisco","seattle","chicago","austin","boston","los angeles","denver","atlanta","dallas","houston","miami","london","berlin","amsterdam","dubai"],s=[["canada","canada"],["ontario","ontario"],["british columbia","bc"],["alberta","alberta"],["quebec","quebec"],["usa","usa"],["united states","usa"]],r=(e+" "+(t||"")).toLowerCase();for(let i of n)r.includes(i)&&o.push(i.replace(/\s+/g,"-"));for(let[i,a]of s)r.includes(i)&&o.push(a);return o}function An(e){let{category:t,situation:o,conversationStatus:n}=e;if(e.dismissedAtPreview&&Re(e.lastMessagePreview)===Re(e.dismissedAtPreview))return{action:"none",detail:e.dismissReason||"Dismissed."};let s=(e.messages||[]).find(r=>ve.has(r.status));return s?s.status===x.CONFIRMED?{action:"push_draft",detail:"Draft confirmed. Run --push-drafts to send."}:{action:"review_draft",detail:"Draft needs review. Run --review-drafts."}:n==="awaiting_reply"?{action:"none",detail:"Ball in their court."}:o==="no_messages"?{action:"none",detail:"No messages read yet."}:o==="they_want_to_schedule_call"?{action:"schedule_call",detail:"They proposed a call. Reply with time slots."}:o==="initial_outreach_unanswered"?t==="spam"?{action:"archive",detail:"Likely spam. Safe to ignore."}:t==="recruiter"||t==="hiring_manager"?{action:"reply",detail:"New recruiter/HM message. Review and respond."}:t==="coach"||t==="sales"?{action:"archive",detail:`${ct(t||"unknown")} outreach. Likely not relevant.`}:{action:"reply",detail:"New message. Review and respond."}:o==="active_conversation_needs_our_reply"||o==="needs_our_reply"?{action:"reply",detail:"They replied. Continue the conversation."}:{action:"none",detail:""}}function no(e,t){if(!e||e.length===0)return{messageCount:0,theirCount:0,ourCount:0,unknownCount:0,hasJob:!1,hasSalary:!1};let o=0,n=0,s=0,r=!1,i=!1,a=e.map(l=>l.text).join(" ");for(let l of e)l.sender===P?o++:l.sender==="Unknown"||l.sender===""?s++:n++;return r=/\b(job|role|position|opening)\b/i.test(a),i=/\$[\d,]+|\d+k|\d+\/hr/i.test(a),{messageCount:e.length,theirCount:n,ourCount:o,unknownCount:s,hasJob:r,hasSalary:i}}function En(e){return e?{title:e.title||null,company:e.company||null,location:e.location||null,connectionStatus:e.connectionStatus||null}:null}function _n(e){let{category:t,situation:o,engagement:n}=e;return o==="they_want_to_schedule_call"?"high":(t==="recruiter"||t==="hiring_manager")&&(o==="active_conversation_needs_our_reply"||o==="needs_our_reply")?n?.messageCount>=4?"high":"medium":t==="recruiter"&&o==="initial_outreach_unanswered"||t==="founder"&&e.conversationStatus==="unanswered_by_us"?"medium":t==="spam"||t==="coach"||t==="sales"?"low":e.conversationStatus==="unanswered_by_us"?"medium":null}function Rn(e,t,o){if(!o)return null;if(t&&o[t])return o[t];if(!e)return null;let n=Object.values(o).filter(s=>s.name===e);return n.length===1?n[0]:null}function Re(e){return(e||"").replace(/\s+/g," ").trim().slice(0,80).toLowerCase()}function ct(e){return e?e.charAt(0).toUpperCase()+e.slice(1):""}function ge(e,t){return e?e.length>t?e.slice(0,t-1)+"\u2026":e:""}function Dn(e){let t=e.match(/\b((?:senior |sr\.? |lead |staff |principal |junior |jr\.? )?(?:software|full.?stack|front.?end|back.?end|data|devops|cloud|platform|mobile|ios|android|qa|sdet)\s*(?:engineer|developer|architect|lead|manager|scientist))\b/i);if(t)return t[1];let o=e.match(/\b((?:java|python|react|node|\.net|golang|rust|scala)\s*developer)\b/i);return o?o[1]:null}function On(e){let t=e.match(/\$[\d,]+(?:k|K)?(?:\s*[-–]\s*\$?[\d,]+(?:k|K)?)?(?:\s*\/\s*(?:hr|hour|yr|year|annum))?/);return t?t[0]:null}function In(e){let t=e.match(/\b(?:in|based in|located in)\s+([A-Z][a-z]+(?:\s[A-Z][a-z]+)?(?:,\s*[A-Z]{2})?)/);return t?t[1]:null}function so(e,t){if(!e)return e;if(e.categorySource!=="manual"){let n=at(e.title);n&&(e.category=n,e.categorySource=e.categorySource||"title")}xn(e),e.personSummary=jn(e),e.personAutoTags=Mn(e);let o=Nn(e);return e.personActionNeeded=o.action,e.personActionDetail=o.detail,e.personPriorityManual||(e.personPriority=Pn(e)),e}function xn(e){if(e.jobCount=e.jobIds?.length||0,e.encounterCount=e.encounters?.length||0,e.encounters?.length>0){let t=e.encounters.filter(o=>o.jobTitle).sort((o,n)=>(n.date||"").localeCompare(o.date||""));t.length>0&&(e.lastJobTitle=t[0].jobTitle,e.lastJobCompany=t[0].jobCompany||null,e.lastJobDate=t[0].date)}e.isRepeatRecruiter=e.jobCount>=3}function jn(e){let t=[],o=e.category,n=e.title,s=e.company,r=e.location;if(o==="recruiter"&&s?t.push(`Recruiter at ${s}`):o==="hiring_manager"&&s?t.push(`Hiring manager at ${s}`):o==="founder"&&s?t.push(`Founder of ${s}`):n&&s?t.push(`${ge(n,40)} at ${ge(s,30)}`):n?t.push(ge(n,50)):e.name&&t.push(e.name.split(" ")[0]),r&&t.push(r),e.connectionDate){let i=new Date(e.connectionDate);if(!isNaN(i)){let a=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];t.push(`Connected ${a[i.getMonth()]} ${i.getDate()}`)}}if(e.jobCount>0){let i=`${e.jobCount} job${e.jobCount>1?"s":""}`;if(e.lastJobTitle){if(i+=` (last: ${ge(e.lastJobTitle,30)}`,e.lastJobDate){let a=new Date(e.lastJobDate);isNaN(a)||(i+=`, ${["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"][a.getMonth()]} ${a.getDate()}`)}i+=")"}t.push(i)}return t.join(". ").replace(/\.\./g,".")||null}function Mn(e){let t=[],o=[e.title,e.company,e.location].filter(Boolean).join(" "),n=oo("",e.location);return t.push(...n),e.category&&t.push(e.category),/\b(java)\b/i.test(o)&&!/javascript/i.test(o)&&t.push("java"),/\b(python)\b/i.test(o)&&t.push("python"),/\b(react|angular|vue|front.?end)\b/i.test(o)&&t.push("frontend"),/\b(node|backend|back.?end|api|microservice)\b/i.test(o)&&t.push("backend"),/\b(full.?stack)\b/i.test(o)&&t.push("fullstack"),/\b(devops|sre|cloud|aws|azure|gcp|kubernetes|k8s)\b/i.test(o)&&t.push("devops"),/\b(data.?engineer|data.?scien|ml|machine.?learn|ai)\b/i.test(o)&&t.push("data/ml"),/\b(lead|senior|staff|principal|architect)\b/i.test(o)&&t.push("senior-role"),/\b(manager|director|vp|head of)\b/i.test(o)&&t.push("management"),/\b(staffing|agency|consulting|outsourc)\b/i.test(o)&&t.push("staffing"),[...new Set(t)]}function Nn(e){if(e.connectionStatus!=="connected")return{action:"none",detail:"Not connected."};let t=e.category;return t==="recruiter"||t==="hiring_manager"?{action:"initiate_contact",detail:`New ${t.replace("_"," ")} connection. Send intro message.`}:t==="founder"?{action:"initiate_contact",detail:"Founder connection. Consider reaching out."}:t==="sales"||t==="coach"||t==="spam"?{action:"none",detail:`${ct(t)} \u2014 no outreach needed.`}:t==="investor"?{action:"initiate_contact",detail:"Investor connection. Consider intro message."}:{action:"none",detail:"No specific action identified."}}function Pn(e){let t=e.category;if(!t)return null;let o=Fn(e,7);return e.isRepeatRecruiter&&(t==="recruiter"||t==="hiring_manager")||(t==="recruiter"||t==="hiring_manager")&&o?"high":t==="recruiter"||t==="hiring_manager"||t==="founder"&&o?"medium":t==="founder"||t==="investor"?"low":t==="sales"||t==="coach"||t==="spam"?null:t==="peer"?"low":null}function Fn(e,t){if(!e.connectionDate)return!1;let o=new Date(e.connectionDate);if(isNaN(o))return!1;let n=new Date;return n.setDate(n.getDate()-t),o>=n}import"dotenv/config";import*as B from"fs";import*as Z from"path";import{fileURLToPath as Hn}from"url";import"dotenv/config";import{createGraphClient as Ln,resolveToken as Un}from"@ilivemylife/graph-sdk";import{config as Bn}from"dotenv";Bn();var ro=Un()||process.env.TOKEN;ro||(console.error(`
|
|
4
|
+
`),await e.close(),process.exit(1)}return{browser:e,page:o}}async function ot(e){await e.goto("https://www.linkedin.com/messaging/",{waitUntil:"domcontentloaded",timeout:6e4}),await _(4e3);let t=e.url();if(t.includes("/login")||t.includes("/authwall"))throw new Error("Redirected to login \u2014 cookies expired. Run: npm run login");try{await e.waitForSelector(".msg-conversations-container__conversations-list, .msg-conversation-listitem",{timeout:15e3})}catch{console.warn(" Inbox container not found via known selectors, proceeding..."),await _(3e3)}console.log(" Inbox loaded.")}async function to(e){let t=await e.evaluate(()=>{let o=document.querySelectorAll(".msg-conversation-listitem"),n=[];for(let s of o){let i=s.querySelector(".msg-conversation-listitem__participant-names, .msg-conversation-card__participant-names")?.textContent?.trim()?.replace(/\s+/g," ")||null;if(!i)continue;let c=s.querySelector(".msg-conversation-card__message-snippet")?.textContent?.trim()?.replace(/\s+/g," ")||"";if(/^\s*Sponsored\b/i.test(c)||/^\s*Promoted\b/i.test(c))continue;let f=s.querySelector(".msg-conversation-listitem__time-stamp, .msg-conversation-card__time-stamp")?.textContent?.trim()||"",d=s.querySelector(".msg-conversation-card__convo-item-container--unread")!==null||s.querySelector(".msg-conversation-card__unread-count")!==null,u,m;if(c.startsWith("You:"))u="me",m=c.slice(4).trim();else{let y=i.split(" ")[0];if(c.startsWith(i+":")||c.startsWith(y+":")){u="them";let v=c.indexOf(":");m=c.slice(v+1).trim()}else u="them",m=c}let S=s.getBoundingClientRect();n.push({name:i,lastMessagePreview:m,lastMessageTime:f,unread:d,lastMessageBy:u,x:S.x+S.width/2,y:S.y+S.height/2})}return n});for(let o of t)o.lastMessageTime=Ge(o.lastMessageTime)||o.lastMessageTime;return t}async function nt(e){return e.evaluate(()=>{let t=[".msg-conversations-container__conversations-list",".msg-conversations-container",'[class*="msg-conversations-container"] ul'];for(let n of t){let s=document.querySelector(n);if(s&&s.scrollHeight>s.clientHeight)return s.scrollBy(0,600),{scrollTop:s.scrollTop,scrollHeight:s.scrollHeight}}let o=document.querySelector(".msg-conversation-listitem");if(o){let n=o.parentElement;for(;n;){if(n.scrollHeight>n.clientHeight+10)return n.scrollBy(0,600),{scrollTop:n.scrollTop,scrollHeight:n.scrollHeight};n=n.parentElement}}return null})}async function kn(e){return e.evaluate(()=>{let t=/^status\s+is\s+|^online$|^offline$|^away$|^busy$|^active\s/i,o=['.msg-overlay-conversation-bubble__header a[href*="/in/"] .truncate','.msg-overlay-conversation-bubble__header a[href*="/in/"]','.msg-overlay-bubble-header__title a[href*="/in/"]',".msg-overlay-bubble-header__title",".msg-overlay-conversation-bubble__header .truncate",".msg-thread__link-to-profile","h2.msg-entity-lockup__entity-title",".msg-s-message-list-container h2",".msg-thread h2",".msg-conversations-container__title-row h2",'.msg-thread a[href*="/in/"]'];for(let s of o){let r=document.querySelector(s);if(r){let i=r.textContent?.trim()?.replace(/\s+/g," ");if(i&&i.length>1&&i.length<100&&!t.test(i))return i}}let n=document.querySelectorAll(".msg-s-message-list-container h2, .msg-thread h2");for(let s of n){let r=s.textContent?.trim();if(r&&r.length>1&&r.length<100&&!/conversation/i.test(r)&&!t.test(r))return r}return null})}async function st(e){return e.evaluate(()=>{let t=[".msg-thread__link-to-profile",'.msg-entity-lockup__entity-title a[href*="/in/"]','.msg-s-message-list-container a[href*="/in/"]','.msg-thread a[href*="/in/"]','.msg-conversations-container__title-row a[href*="/in/"]','.msg-overlay-conversation-bubble__header a[href*="/in/"]','.msg-overlay-bubble-header a[href*="/in/"]','.msg-overlay-conversation-bubble a[href*="/in/"]'];for(let o of t){let n=document.querySelector(o);if(n){let s=n.getAttribute("href")||"";if(s&&s.includes("/in/"))return s.startsWith("/")&&(s="https://www.linkedin.com"+s),s=s.replace(/\/$/,"").split("?")[0],s}}return null})}async function Qt(e,t){let o=await kn(e);if(!o)return{match:!1,headerName:null};let n=t.split(" ")[0].toLowerCase(),s=t.toLowerCase().split(/[\s,]+/).filter(a=>a.length>1),r=o.toLowerCase();return{match:r.includes(n)||s.some(a=>r.includes(a)),headerName:o}}async function Fe(e,t){let o=t.split(" ")[0],n=8e3,s=500,r=0;for(;r<n;){let{match:c,headerName:l}=await Qt(e,t);if(c)return await _(500),{verified:!0,headerName:l};let f=await e.evaluate(()=>{let d=document.querySelectorAll(".msg-s-message-group__name"),u=Array.from(d).map(S=>S.textContent?.trim()).filter(Boolean),m=document.querySelectorAll(".msg-s-event-listitem").length;return{allSenders:u,msgCount:m}});if(f.msgCount>0&&f.allSenders.some(u=>u.toLowerCase().includes(o.toLowerCase())))return await _(300),{verified:!0,headerName:o};await _(s),r+=s}let{match:i,headerName:a}=await Qt(e,t);return i?{verified:!0,headerName:a}:{verified:!1,headerName:a}}async function Cn(e){let o=0,n=0;for(let s=0;s<50;s++){let r=await e.evaluate(()=>{let i=document.querySelector(".msg-thread .msg-s-message-list-container")||document.querySelector(".msg-s-message-list-container");if(!i){let l=document.querySelector(".msg-overlay-conversation-bubble");l&&(i=l.querySelector(".msg-s-message-list-container")||l.querySelector(".msg-s-message-list"))}if(!i)return{found:!1};let a=i.querySelectorAll("li.msg-s-message-list__event").length;return i.scrollTop<=1?{found:!0,atTop:!0,msgCount:a}:(i.scrollTop=0,{found:!0,atTop:!1,msgCount:a})});if(!r.found||r.atTop)break;if(await _(800),r.msgCount===o){if(n++,n>=3)break}else n=0;o=r.msgCount}}async function rt(e,{scrollUp:t=!0}={}){t&&await Cn(e);let o=await e.evaluate(()=>{let n=[],s="",r=document.querySelector(".msg-thread .msg-s-message-list-container")||document.querySelector(".msg-s-message-list-container");if(!r){let f=document.querySelector(".msg-overlay-conversation-bubble");f&&(r=f.querySelector(".msg-s-message-list-container")||f.querySelector(".msg-s-message-list"))}if(!r)return n;let i=r.querySelectorAll("li.msg-s-message-list__event");if(i.length===0){let f=document.querySelector(".msg-overlay-conversation-bubble, .msg-overlay-list-bubble");f&&(i=f.querySelectorAll("li.msg-s-message-list__event, .msg-s-event-listitem"))}function a(f){let d=f.getAttribute("data-event-urn");if(!d)return null;let u=d.match(/,2-([A-Za-z0-9+/=]+)\)/);if(!u)return null;try{let S=atob(u[1]).match(/^(\d{13})/);if(!S)return null;let y=Number(S[1]);if(y>14200704e5&&y<20512224e5)return y}catch{}return null}let c="";function l(f,d,u){let m=a(f),y=f.querySelector(".msg-s-message-group__timestamp, time")?.textContent?.trim()||"",v=f.querySelector(".msg-s-event-listitem__message-bubble");if(!v)return null;let $=v.textContent?.trim()?.replace(/Open Emoji Keyboard|Reply to this message|React with[^.]+/g,"")?.replace(/^(?:👏\s*👍\s*😊\s*)+/,"")?.replace(/\s+/g," ")?.trim()||"";if(!$)return null;let k=m?new Date(m).toISOString():u?`${u} ${y}`.trim():y;return{sender:d,text:$,time:k}}for(let f of i){let d=f.querySelector("time.msg-s-message-list__time-heading");d&&(c=d.textContent?.trim()||c);let u=f.querySelectorAll(".msg-s-message-group");if(u.length>0)for(let m of u){let S=m.querySelector(".msg-s-message-group__name");S&&(s=S.textContent?.trim()||s);let y=s||"Unknown",v=m.querySelectorAll(".msg-s-event-listitem");for(let $ of v){let k=l($,y,c);k&&n.push(k)}}else{let m=f.querySelectorAll(".msg-s-event-listitem");for(let S of m){let y=S.querySelector(".msg-s-message-group__name");y&&(s=y.textContent?.trim()||s);let $=l(S,s||"Unknown",c);$&&n.push($)}}}if(n.length===0){let f=document.querySelectorAll(".msg-s-event-listitem--group-a11y-heading");for(let d of f){let u=d.textContent?.trim();u&&n.push({sender:"",text:u,time:""})}}return n});for(let n of o)(!n.time||!/^\d{4}-\d{2}-\d{2}T/.test(n.time))&&(n.time=Ge(n.time)||n.time);return o}function it(e,t,o){if(e.length===0)return!0;let n=t.split(" ")[0].toLowerCase(),s=t.toLowerCase().split(/[\s,]+/).filter(l=>l.length>1),r=[...new Set(e.map(l=>l.sender).filter(l=>l&&l!=="Unknown"))],i=r.some(l=>{let f=l.toLowerCase();return f.includes(n)||s.some(d=>f.includes(d))}),a=P.toLowerCase().split(/\s+/),c=r.every(l=>{let f=l.toLowerCase();return a.some(d=>f.includes(d))});if(i)return!0;if(c){for(let l of e){let d=(l.text||"").trim().match(/^(?:Hi|Hey|Hello|Dear)\s+([A-Z][a-z]+)/);if(d){let u=d[1].toLowerCase();if(u!=="there"&&u!=="all"&&!(s.some(S=>S===u)||u===n))return console.log(` STALE DATA: message says "Hi ${d[1]}" but expected "${t}"`),!1}}return!0}if(o?.length>0){let l=o.map(d=>d.toLowerCase());if(r.some(d=>{let u=d.toLowerCase();return l.some(m=>u.includes(m.split(" ")[0]))}))return console.log(` STALE DATA: messages belong to previous conversation (senders: ${r.join(", ")})`),!1}return console.log(` Sender warning: no match for "${t}" (found: ${r.join(", ")})`),!1}function ge(e){return(e||"").replace(/\s+/g," ").trim().toLowerCase()}function at(e,t){if(!e||e.length===0)return t||[];if(!t||t.length===0)return e;let o=u=>ge(u.sender)+"||"+ge(u.text),n=new Set(t.map(o)),s=new Set(e.map(o)),r=e.filter(u=>fe.has(u.status)?!1:!n.has(o(u))),i=t.filter(u=>!s.has(o(u))),a=t.filter(u=>s.has(o(u))),c=e.filter(u=>fe.has(u.status)),l=[...r,...a,...i],f=[];for(let u of l){let m=ge(u.text),S=ge(u.sender),y=!1;for(let v=0;v<f.length;v++){let $=f[v];if(ge($.sender)!==S)continue;let k=ge($.text);if(m.length>k.length&&m.endsWith(k)){y=!0;break}if(k.length>m.length&&k.endsWith(m)){f[v]=u,y=!0;break}}y||f.push(u)}return[...f,...c]}async function oo(e,t){await ot(e),await _(2500),await e.evaluate(()=>{let a=[".msg-conversations-container__conversations-list",".msg-conversations-container"];for(let c of a){let l=document.querySelector(c);if(l){l.scrollTop=0;return}}}),await _(500);async function o(){return e.evaluate(a=>{let c=document.querySelectorAll(".msg-conversation-listitem");for(let l of c)if(l.querySelector(".msg-conversation-listitem__participant-names")?.textContent?.trim()?.replace(/\s+/g," ")===a){let u=l.getBoundingClientRect();if(u.height>0)return{x:u.x+u.width/2,y:u.y+u.height/2}}return null},t)}let n=await o(),s=50,r=-1,i=0;for(let a=0;a<s&&!n;a++){let c=await nt(e);if(!c)break;if(c.scrollTop===r){if(i++,i>=2)break}else i=0,r=c.scrollTop;await _(600),n=await o()}return n?(await e.mouse.click(n.x,n.y),await _(3e3),Fe(e,t)):{verified:!1,headerName:null}}async function ct(e,t){let o=await e.evaluate(i=>{let a=document.querySelectorAll(".msg-conversation-listitem");for(let c of a)if(c.querySelector(".msg-conversation-listitem__participant-names")?.textContent?.trim()?.replace(/\s+/g," ")===i){let d=c.getBoundingClientRect();if(d.height>0)return{x:d.x+d.width/2,y:d.y+d.height/2}}return null},t);if(o){await e.mouse.click(o.x,o.y),await _(3e3);let i=await Fe(e,t);if(i.verified)return i}let n=Dt(t),s=n!==t?[t,n]:[t];for(let i of s)if(await Xt(e,i,t)){let c=await Fe(e,t);if(c.verified)return c}let r=Ot(n);if(r!==n&&r.length>0&&await Xt(e,r,t,{requireUnique:!0})){let a=await Fe(e,t);if(a.verified)return a}return{verified:!1,headerName:null}}async function Xt(e,t,o,n={}){let{requireUnique:s=!1}=n,r=await e.$("input#search-conversations, .msg-cross-pillar-search-form__search-field");if(!r)return!1;await r.click(),await _(300),await e.keyboard.down("Control"),await e.keyboard.press("a"),await e.keyboard.up("Control"),await e.keyboard.press("Backspace"),await _(300),await r.type(t,{delay:30}),await _(500),await e.keyboard.press("Enter"),await _(3500);let i=await e.evaluate((a,c)=>{let l=a.split(" ")[0].toLowerCase(),f=a.toLowerCase(),d=document.querySelectorAll(".msg-conversation-listitem"),u=[],m=[];for(let y of d){let $=y.querySelector(".msg-conversation-listitem__participant-names, .msg-conversation-card__participant-names")?.textContent?.trim()?.replace(/\s+/g," ")||"",k=$.toLowerCase();if(k.includes(l)){let g=y.getBoundingClientRect();if(g.height>0){let p=$.includes(",")||/\d+ other/.test($),w=k===f?3:k.includes(f)?2:1,C={text:$,isGroup:p,score:w,x:g.x+g.width/2,y:g.y+g.height/2};p?m.push(C):u.push(C)}}}u.sort((y,v)=>v.score-y.score);let S=[...u,...m];return c&&S.length!==1?null:u[0]||m[0]||null},o,s);return i?(s&&console.log(" (found via stripped designations)"),await e.mouse.click(i.x,i.y),!0):!1}async function Z(e){let t=await e.$("input#search-conversations, .msg-cross-pillar-search-form__search-field");t&&(await t.click(),await _(200),await e.keyboard.down("Control"),await e.keyboard.press("a"),await e.keyboard.up("Control"),await e.keyboard.press("Backspace"),await e.keyboard.press("Escape"),await _(1e3))}function ut(e,t,o){if(!e)return e;let s=(e.messages||[]).filter(c=>!fe.has(c.status)),r=In(t,e.profileUrl,o),i=ft(s,t);if(e.lastMessageBy=i,e.conversationStatus=i==="me"?"awaiting_reply":"unanswered_by_us",e.categorySource!=="manual"){let c=Tn(s,t,r);c&&(e.category=c,e.categorySource=e.categorySource||"message_content")}if(e.situation=En(s,i,t),s.length>0){let c=s[s.length-1];e.lastMessageTime=c.time}if(s.length>0&&!e.initiatedBy){let c=s[0];c.sender===P?e.initiatedBy="us":c.sender!=="Unknown"&&c.sender!==""&&(e.initiatedBy="them")}e.engagement=io(s,t),e.personContext=Dn(r),e.summary=_n(e,t,r),e.autoTags=An(e,t,r);let a=Rn(e);return e.actionNeeded=a.action,e.actionDetail=a.detail,e.suggestedPriority=On(e),e}var so={recruiter:{theirMessages:[/\b(recruiter|recruiting|talent.?acqui|staffing|headhunter|head\s*hunter|sourcer|executive\s*search)\b/i],title:[/\b(recruiter|recruiting|talent.?acqui|staffing|sourcer|sourcing)\b/i,/\b(head\s*hunter|executive\s*search)\b/i]},hiring_manager:{theirMessages:[/\b(my|our)\s+team\b/i,/\b(i.?m|we.?re|i\s+am|we\s+are)\s+(currently\s+|actively\s+|now\s+)?(looking|hiring|recruiting)\b/i,/\bjoin\s+(my|our)\b/i,/\bdirect.?report/i,/\b(engineering manager|team lead|vp of|director of|head of)\b/i],title:[/\b(engineering manager|team lead|vp|director|head of engineering)\b/i]},founder:{theirMessages:[/\b(founder|co-founder|ceo|cto|my company|my startup|i.?m building)\b/i],title:[/\b(founder|co-founder|ceo|cto)\b/i]},investor:{theirMessages:[/\b(investor|invest|funding|vc|venture|angel)\b/i],title:[/\b(investor|venture|capital|angel|partner at)\b/i,/\binvestment\s+(manager|partner|director|associate|analyst|principal)\b/i]},coach:{theirMessages:[/\b(coach|coaching|mentor|mentoring|career.?(advice|guidance|help))\b/i,/\b(resume.?(review|writing)|interview.?prep|career.?transition)\b/i],title:[/\b(coach|mentor|career.?(consultant|advisor))\b/i]},peer:{theirMessages:[/\b(fellow|peer|networking|connect|nice.?to.?meet|great.?to.?connect)\b/i,/\b(your (profile|background|experience).*interest)\b/i]},sales:{theirMessages:[/\b(sales|business develop|partnership|proposal|offer|demo|pricing|platform|solution)\b/i],title:[/\b(sales|business develop|account.?exec|bdm|bdr|sdr)\b/i]},spam:{theirMessages:[/\b(crypto|bitcoin|forex|trading.?signal|guaranteed.?return|make.?money)\b/i,/\b(whatsapp|telegram.?group|click (here|this link))\b/i]},event:{theirMessages:[/\b(event|conference|webinar|summit|meetup|workshop|speak(er|ing))\b/i,/\b(attend|register|rsvp|invitation|invite.?you)\b/i]}};function Tn(e,t,o){if(!e||e.length===0)return o?.title?lt(o.title):null;let n=e.filter(s=>s.sender!==P&&s.sender!=="Unknown"&&s.sender!=="").map(s=>s.text).join(" ");for(let[s,r]of Object.entries(so))if(r.theirMessages){for(let i of r.theirMessages)if(i.test(n))return s}if(o?.title){let s=lt(o.title);if(s)return s}return null}function lt(e){if(!e)return null;for(let[t,o]of Object.entries(so))if(o.title){for(let n of o.title)if(n.test(e))return t}return null}var no=/\b(call|meet|teams|zoom|schedule|available|availability|invite|slot|calendar)\b/i;function En(e,t,o){if(!e||e.length===0)return"no_messages";let n=e[e.length-1];if(e.length===1)return t==="them"?"initial_outreach_unanswered":"we_initiated_waiting";if(t==="me")return no.test(n.text)?"scheduling_proposed_by_us":e.length>=6?"active_conversation_ball_in_their_court":"awaiting_their_reply";let s=[...e].reverse().find(r=>r.sender!==P&&r.sender!=="Unknown");return no.test(s?.text||"")?"they_want_to_schedule_call":e.length>=6?"active_conversation_needs_our_reply":"needs_our_reply"}function ft(e,t){if(!e||e.length===0)return"them";let o=[...e].map(s=>({m:s,t:Date.parse(s.time)})).filter(s=>Number.isFinite(s.t)).sort((s,r)=>r.t-s.t).map(s=>s.m),n=o.length>0?o:[...e].reverse();for(let s of n){let r=s.sender;if(r===P)return"me";if(r!=="Unknown"&&r!=="")return"them"}return"them"}function _n(e,t,o){let n=e.messages||[];if(n.length===0)return null;let s=[],r=e.category,i=o?.title,a=o?.company,c=o?.location;r==="recruiter"&&a?s.push(`Recruiter from ${a}`):r==="hiring_manager"&&a?s.push(`Hiring manager at ${a}`):r==="founder"&&a?s.push(`Founder of ${a}`):r&&i?s.push(`${dt(r)}: ${me(i,40)}`):i?s.push(me(i,50)):s.push(t.split(" ")[0]);let l=n.filter(S=>S.sender!==P&&S.sender!=="Unknown").map(S=>S.text).join(" "),f=xn(l),d=jn(l),u=Nn(l),m=[];return f&&m.push(f),u&&u!==c&&m.push(u),d&&m.push(d),m.length>0&&s.push(m.join(", ")),e.situation==="they_want_to_schedule_call"?s.push("wants to schedule call"):e.situation==="scheduling_proposed_by_us"?s.push("we proposed scheduling"):e.conversationStatus==="unanswered_by_us"?s.push("needs reply"):e.conversationStatus==="awaiting_reply"&&s.push("waiting on them"),s.join(". ").replace(/\.\./g,".")}function An(e,t,o){let n=[],s=(e.messages||[]).filter(l=>!fe.has(l.status)),r=s.map(l=>l.text).join(" "),i=s.filter(l=>l.sender!==P&&l.sender!=="Unknown").map(l=>l.text).join(" "),a=ro(i,o?.location);n.push(...a),/\b(contract|contractor|c2c|corp.?to.?corp)\b/i.test(r)&&n.push("contract"),/\b(full.?time|permanent|fte|perm)\b/i.test(r)&&n.push("full-time"),/\b(remote|work.?from.?home|wfh)\b/i.test(r)&&n.push("remote"),/\b(hybrid)\b/i.test(r)&&n.push("hybrid"),/\b(on.?site|in.?office|in.?person)\b/i.test(r)&&n.push("onsite"),/\b(java)\b/i.test(r)&&!/javascript/i.test(r)&&n.push("java"),/\b(python)\b/i.test(r)&&n.push("python"),/\b(react|angular|vue|front.?end)\b/i.test(r)&&n.push("frontend"),/\b(node|backend|back.?end|api|microservice)\b/i.test(r)&&n.push("backend"),/\b(full.?stack)\b/i.test(r)&&n.push("fullstack"),/\b(devops|sre|cloud|aws|azure|gcp|kubernetes|k8s)\b/i.test(r)&&n.push("devops"),/\b(data.?engineer|data.?scien|ml|machine.?learn|ai)\b/i.test(r)&&n.push("data/ml"),/\b(lead|senior|staff|principal|architect)\b/i.test(r)&&n.push("senior-role"),/\b(manager|director|vp|head of)\b/i.test(r)&&n.push("management"),/\$[\d,]+|\d+k|\d+\/hr|\d+\s*per\s*(hour|annum|year)/i.test(r)&&n.push("has-salary");let c=e.engagement||io(s,t);return c.messageCount>=6&&n.push("active-thread"),c.theirCount===0&&c.ourCount>0&&n.push("no-response"),c.ourCount===0&&c.theirCount>0&&n.push("unanswered"),e.situation==="they_want_to_schedule_call"&&n.push("hot-lead"),(e.messages||[]).some(l=>$e.has(l.status))&&n.push("has-draft"),[...new Set(n)]}function ro(e,t){let o=[],n=["toronto","vancouver","montreal","ottawa","calgary","edmonton","new york","san francisco","seattle","chicago","austin","boston","los angeles","denver","atlanta","dallas","houston","miami","london","berlin","amsterdam","dubai"],s=[["canada","canada"],["ontario","ontario"],["british columbia","bc"],["alberta","alberta"],["quebec","quebec"],["usa","usa"],["united states","usa"]],r=(e+" "+(t||"")).toLowerCase();for(let i of n)r.includes(i)&&o.push(i.replace(/\s+/g,"-"));for(let[i,a]of s)r.includes(i)&&o.push(a);return o}function Rn(e){let{category:t,situation:o,conversationStatus:n}=e;if(e.dismissedAtPreview&&De(e.lastMessagePreview)===De(e.dismissedAtPreview))return{action:"none",detail:e.dismissReason||"Dismissed."};let s=(e.messages||[]).find(r=>$e.has(r.status));return s?s.status===j.CONFIRMED?{action:"push_draft",detail:"Draft confirmed. Run --push-drafts to send."}:{action:"review_draft",detail:"Draft needs review. Run --review-drafts."}:n==="awaiting_reply"?{action:"none",detail:"Ball in their court."}:o==="no_messages"?{action:"none",detail:"No messages read yet."}:o==="they_want_to_schedule_call"?{action:"schedule_call",detail:"They proposed a call. Reply with time slots."}:o==="initial_outreach_unanswered"?t==="spam"?{action:"archive",detail:"Likely spam. Safe to ignore."}:t==="recruiter"||t==="hiring_manager"?{action:"reply",detail:"New recruiter/HM message. Review and respond."}:t==="coach"||t==="sales"?{action:"archive",detail:`${dt(t||"unknown")} outreach. Likely not relevant.`}:{action:"reply",detail:"New message. Review and respond."}:o==="active_conversation_needs_our_reply"||o==="needs_our_reply"?{action:"reply",detail:"They replied. Continue the conversation."}:{action:"none",detail:""}}function io(e,t){if(!e||e.length===0)return{messageCount:0,theirCount:0,ourCount:0,unknownCount:0,hasJob:!1,hasSalary:!1};let o=0,n=0,s=0,r=!1,i=!1,a=e.map(c=>c.text).join(" ");for(let c of e)c.sender===P?o++:c.sender==="Unknown"||c.sender===""?s++:n++;return r=/\b(job|role|position|opening)\b/i.test(a),i=/\$[\d,]+|\d+k|\d+\/hr/i.test(a),{messageCount:e.length,theirCount:n,ourCount:o,unknownCount:s,hasJob:r,hasSalary:i}}function Dn(e){return e?{title:e.title||null,company:e.company||null,location:e.location||null,connectionStatus:e.connectionStatus||null}:null}function On(e){let{category:t,situation:o,engagement:n}=e;return o==="they_want_to_schedule_call"?"high":(t==="recruiter"||t==="hiring_manager")&&(o==="active_conversation_needs_our_reply"||o==="needs_our_reply")?n?.messageCount>=4?"high":"medium":t==="recruiter"&&o==="initial_outreach_unanswered"||t==="founder"&&e.conversationStatus==="unanswered_by_us"?"medium":t==="spam"||t==="coach"||t==="sales"?"low":e.conversationStatus==="unanswered_by_us"?"medium":null}function In(e,t,o){if(!o)return null;if(t&&o[t])return o[t];if(!e)return null;let n=Object.values(o).filter(s=>s.name===e);return n.length===1?n[0]:null}function De(e){return(e||"").replace(/\s+/g," ").trim().slice(0,80).toLowerCase()}function dt(e){return e?e.charAt(0).toUpperCase()+e.slice(1):""}function me(e,t){return e?e.length>t?e.slice(0,t-1)+"\u2026":e:""}function xn(e){let t=e.match(/\b((?:senior |sr\.? |lead |staff |principal |junior |jr\.? )?(?:software|full.?stack|front.?end|back.?end|data|devops|cloud|platform|mobile|ios|android|qa|sdet)\s*(?:engineer|developer|architect|lead|manager|scientist))\b/i);if(t)return t[1];let o=e.match(/\b((?:java|python|react|node|\.net|golang|rust|scala)\s*developer)\b/i);return o?o[1]:null}function jn(e){let t=e.match(/\$[\d,]+(?:k|K)?(?:\s*[-–]\s*\$?[\d,]+(?:k|K)?)?(?:\s*\/\s*(?:hr|hour|yr|year|annum))?/);return t?t[0]:null}function Nn(e){let t=e.match(/\b(?:in|based in|located in)\s+([A-Z][a-z]+(?:\s[A-Z][a-z]+)?(?:,\s*[A-Z]{2})?)/);return t?t[1]:null}function ao(e,t){if(!e)return e;if(e.categorySource!=="manual"){let n=lt(e.title);n&&(e.category=n,e.categorySource=e.categorySource||"title")}Mn(e),e.personSummary=Pn(e),e.personAutoTags=Fn(e);let o=Ln(e);return e.personActionNeeded=o.action,e.personActionDetail=o.detail,e.personPriorityManual||(e.personPriority=Un(e)),e}function Mn(e){if(e.jobCount=e.jobIds?.length||0,e.encounterCount=e.encounters?.length||0,e.encounters?.length>0){let t=e.encounters.filter(o=>o.jobTitle).sort((o,n)=>(n.date||"").localeCompare(o.date||""));t.length>0&&(e.lastJobTitle=t[0].jobTitle,e.lastJobCompany=t[0].jobCompany||null,e.lastJobDate=t[0].date)}e.isRepeatRecruiter=e.jobCount>=3}function Pn(e){let t=[],o=e.category,n=e.title,s=e.company,r=e.location;if(o==="recruiter"&&s?t.push(`Recruiter at ${s}`):o==="hiring_manager"&&s?t.push(`Hiring manager at ${s}`):o==="founder"&&s?t.push(`Founder of ${s}`):n&&s?t.push(`${me(n,40)} at ${me(s,30)}`):n?t.push(me(n,50)):e.name&&t.push(e.name.split(" ")[0]),r&&t.push(r),e.connectionDate){let i=new Date(e.connectionDate);if(!isNaN(i)){let a=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];t.push(`Connected ${a[i.getMonth()]} ${i.getDate()}`)}}if(e.jobCount>0){let i=`${e.jobCount} job${e.jobCount>1?"s":""}`;if(e.lastJobTitle){if(i+=` (last: ${me(e.lastJobTitle,30)}`,e.lastJobDate){let a=new Date(e.lastJobDate);isNaN(a)||(i+=`, ${["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"][a.getMonth()]} ${a.getDate()}`)}i+=")"}t.push(i)}return t.join(". ").replace(/\.\./g,".")||null}function Fn(e){let t=[],o=[e.title,e.company,e.location].filter(Boolean).join(" "),n=ro("",e.location);return t.push(...n),e.category&&t.push(e.category),/\b(java)\b/i.test(o)&&!/javascript/i.test(o)&&t.push("java"),/\b(python)\b/i.test(o)&&t.push("python"),/\b(react|angular|vue|front.?end)\b/i.test(o)&&t.push("frontend"),/\b(node|backend|back.?end|api|microservice)\b/i.test(o)&&t.push("backend"),/\b(full.?stack)\b/i.test(o)&&t.push("fullstack"),/\b(devops|sre|cloud|aws|azure|gcp|kubernetes|k8s)\b/i.test(o)&&t.push("devops"),/\b(data.?engineer|data.?scien|ml|machine.?learn|ai)\b/i.test(o)&&t.push("data/ml"),/\b(lead|senior|staff|principal|architect)\b/i.test(o)&&t.push("senior-role"),/\b(manager|director|vp|head of)\b/i.test(o)&&t.push("management"),/\b(staffing|agency|consulting|outsourc)\b/i.test(o)&&t.push("staffing"),[...new Set(t)]}function Ln(e){if(e.connectionStatus!=="connected")return{action:"none",detail:"Not connected."};let t=e.category;return t==="recruiter"||t==="hiring_manager"?{action:"initiate_contact",detail:`New ${t.replace("_"," ")} connection. Send intro message.`}:t==="founder"?{action:"initiate_contact",detail:"Founder connection. Consider reaching out."}:t==="sales"||t==="coach"||t==="spam"?{action:"none",detail:`${dt(t)} \u2014 no outreach needed.`}:t==="investor"?{action:"initiate_contact",detail:"Investor connection. Consider intro message."}:{action:"none",detail:"No specific action identified."}}function Un(e){let t=e.category;if(!t)return null;let o=Bn(e,7);return e.isRepeatRecruiter&&(t==="recruiter"||t==="hiring_manager")||(t==="recruiter"||t==="hiring_manager")&&o?"high":t==="recruiter"||t==="hiring_manager"||t==="founder"&&o?"medium":t==="founder"||t==="investor"?"low":t==="sales"||t==="coach"||t==="spam"?null:t==="peer"?"low":null}function Bn(e,t){if(!e.connectionDate)return!1;let o=new Date(e.connectionDate);if(isNaN(o))return!1;let n=new Date;return n.setDate(n.getDate()-t),o>=n}import"dotenv/config";import*as B from"fs";import*as ee from"path";import{fileURLToPath as Yn}from"url";import"dotenv/config";import{createGraphClient as qn,resolveToken as Jn}from"@ilivemylife/graph-sdk";import{config as Hn}from"dotenv";Hn();var co=Jn()||process.env.TOKEN;co||(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
|
|
8
|
-
`),process.exit(1));var
|
|
8
|
+
`),process.exit(1));var lo=qn({token:co});function Vn(e){if(!e||e<1e3)return`${e||0}ms`;let t=Math.round(e/1e3);if(t<60)return`${t}s`;let o=Math.floor(t/60),n=t%60;return n?`${o}m ${n}s`:`${o}m`}function Wn({scriptName:e,mode:t,status:o,error:n,duration:s}){let r=o==="error"?"\u2717":o==="quota-reached"?"\u26A0":"\u2713",i=new Date().toISOString().slice(0,16).replace("T"," "),a=s?` ${Vn(s)}`:"",c=o==="error"?`error${n?`: ${String(n).slice(0,80)}`:""}`:o==="quota-reached"?"daily quota reached":"done",l=t&&t!=="default"?` (${t})`:"";return`${r} ${e}${l} \u2014 ${c} \u2014 ${i}${a}`}async function uo({scriptName:e,mode:t,status:o,summaryLines:n,error:s,duration:r}){let i=process.env.NODE_RUN_REPORTS;if(!i||!Array.isArray(n)||n.length===0)return;let c=`**${Wn({scriptName:e,mode:t,status:o,error:s,duration:r})}**
|
|
9
9
|
|
|
10
10
|
${n.join(`
|
|
11
|
-
`)}`;try{await
|
|
12
|
-
`)}import"dotenv/config";import*as b from"fs";import*as
|
|
11
|
+
`)}`;try{await lo.addMessage(i,c)}catch(l){console.error(`[runReport] Failed to post end-of-session message: ${l.message}`)}}var Gn=Yn(import.meta.url),zn=ee.dirname(ee.dirname(Gn)),pt=process.env.DATA_DIR?ee.resolve(process.env.DATA_DIR,"collected-profiles"):ee.join(zn,"collected-profiles"),gt=ee.join(pt,"run-log.json");function Kn(e,t){let o=JSON.stringify(t,null,2),n=e+".tmp";B.writeFileSync(n,o);for(let s=0;s<3;s++)try{B.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 Le(){try{if(!B.existsSync(gt))return{runs:[]};let e=JSON.parse(B.readFileSync(gt,"utf-8"));return!e.runs||!Array.isArray(e.runs)?{runs:[]}:e}catch{return{runs:[]}}}function fo(e){B.existsSync(pt)||B.mkdirSync(pt,{recursive:!0});try{Kn(gt,e)}catch(t){console.error(`[!] Failed to save run-log.json: ${t.message}`)}}function Ue(e,t="default"){let o=Le();for(let i of o.runs)i.status==="running"&&(i.status="interrupted",i.result="interrupted",i.timestamp&&(i.duration=Date.now()-new Date(i.timestamp).getTime()));let n=new Date,s={timestamp:n.toISOString(),script:e,mode:t,status:"running",result:null,duration:null,stats:null,error:null};o.runs.push(s);let r=Date.now()-30*24*60*60*1e3;return o.runs=o.runs.filter(i=>new Date(i.timestamp).getTime()>r),fo(o),{script:e,mode:t,startTime:n.getTime(),runIndex:o.runs.length-1}}function he(e,t,o=null,n=null){if(!e)return;let s=null;try{let i=Le(),a=i.runs[e.runIndex];if(a&&a.status==="running"&&a.script===e.script)a.status=t,a.result=t,a.duration=Date.now()-e.startTime,a.stats=o,a.error=n,s=a.duration;else{let c=i.runs.find(l=>l.script===e.script&&l.status==="running"&&l.timestamp===new Date(e.startTime).toISOString());c&&(c.status=t,c.result=t,c.duration=Date.now()-e.startTime,c.stats=o,c.error=n,s=c.duration)}fo(i)}catch(i){console.error(`[runLog] Failed to end run: ${i.message}`)}let r=o&&Array.isArray(o.summaryLines)?o.summaryLines:null;if(r&&r.length>0&&process.env.NODE_RUN_REPORTS){let i=t==="error"?"error":o&&o.dailyLimitHit?"quota-reached":"done";uo({scriptName:e.script,mode:e.mode,status:i,summaryLines:r,error:n,duration:s??Date.now()-e.startTime}).catch(a=>console.error(`[runLog] postRunReport failed: ${a.message}`))}}function Qn(){let e=new Date().toISOString().slice(0,10),t=Le(),o={};for(let n of t.runs){if(!n.timestamp.startsWith(e))continue;o[n.script]||(o[n.script]={total:0,success:0,error:0,interrupted:0,partial:0,running:0,lastRun:null});let s=o[n.script];s.total++,n.status==="success"?s.success++:n.status==="error"?s.error++:n.status==="interrupted"?s.interrupted++:n.status==="partial"?s.partial++:n.status==="running"&&s.running++,s.lastRun=n}return o}function Xn(e){if(!e)return"never";let t=Date.now()-new Date(e).getTime(),o=Math.floor(t/6e4),n=Math.floor(t/36e5),s=Math.floor(t/864e5);return o<1?"just now":o<60?`${o}m ago`:n<24?`${n}h ago`:s===1?"yesterday":`${s} days ago`}function po(){let e=["sync-all","messages","funnel","sync","apply"],t=Le(),o=Qn(),n=[];n.push("\u2550\u2550\u2550 SCRIPT STATUS \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550");for(let r of e){let i=t.runs.filter(y=>y.script===r),a=[...i].reverse().find(y=>y.status==="success"),c=i[i.length-1],l=a||c,f=l?Xn(l.timestamp):"never",u=(l?Math.floor((Date.now()-new Date(l.timestamp).getTime())/864e5):1/0)>1?" \u26A0":l?.status==="error"?" \u2717":" \u2713",m="";if(l?.stats){let y=l.stats,v=Object.entries(y).filter(([,$])=>typeof $=="number"&&$>0).slice(0,3).map(([$,k])=>`${k} ${$}`);v.length&&(m=` (${v.join(", ")})`)}let S=l?.duration?` ${Math.round(l.duration/1e3)}s`:"";n.push(` ${r.padEnd(14)} ${f.padEnd(16)}${S}${m}${u}`)}let s=Object.entries(o);if(s.length>0){n.push("");for(let[r,i]of s){let a=[];i.success&&a.push(`${i.success} success`),i.error&&a.push(`${i.error} error`),i.interrupted&&a.push(`${i.interrupted} interrupted`),i.running&&a.push(`${i.running} running`),n.push(` Today: ${r} \xD7${i.total} (${a.join(", ")})`)}}return n.push("\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550"),n.join(`
|
|
12
|
+
`)}import"dotenv/config";import*as b from"fs";import*as vt from"os";import*as A from"path";import{fileURLToPath as hs}from"url";var yt={};We(yt,{NAME:()=>es,VERSION:()=>Zn,up:()=>ns});import*as q from"fs";import*as Be from"path";var Zn=1,es="normalize-jobs-and-questions",ts=["description","techStack","jobTags","seniority","employmentType","industries","jobFunction","postedTime","applicantCount","companySize","workplaceType","contractType","isEasyApply","applyType","companyUrl","scoutedAt","lastSeen"],os=50;function mt(e){return q.existsSync(e)?JSON.parse(q.readFileSync(e,"utf-8")):null}function ht(e,t){let o=JSON.stringify(t,null,2),n=e+".tmp";try{q.writeFileSync(n,o)}catch(s){try{q.unlinkSync(n)}catch{}throw s}try{q.renameSync(n,e)}catch(s){try{q.unlinkSync(n)}catch{}if(s.code==="EPERM"||s.code==="EBUSY"){let r=new Error(`File locked: ${e} (${s.code}). On Windows usually antivirus / file explorer / open editor. Close it and re-run.`);throw r.code=s.code,r}throw s}}async function ns(e,t){let o=Be.join(e,"jobs.json"),n=Be.join(t,"scouted-jobs.json"),s=Be.join(e,"application-questions.json"),r=mt(o)||{},i=mt(n)||{},a=0,c=[];for(let f of Object.keys(r)){if(!i[f])continue;let d=i[f],u=r[f];for(let m of ts)d[m]!=null&&u[m]==null&&(u[m]=d[m]);c.push(f),a++}a>0?(ht(o,r),console.log(` Phase 1: Merged ${a} scouted jobs into jobs.json`)):console.log(" Phase 1: No overlapping jobs to merge");let l=mt(s);if(l&&Array.isArray(l.applications)){let f={},d=0;for(let u of l.applications){let m=u.jobId||"unknown";f[m]||(f[m]={applications:[]});let S=(u.questions||[]).map(y=>{let v={type:y.type||"input",question:y.question||"",answer:y.answer||""};return y.options&&(y.options.length<=os?v.options=y.options:(v.optionsOmitted=!0,d++)),y.page&&(v.page=y.page),y.wasPreFilled&&(v.wasPreFilled=!0),y.wasRetry&&(v.wasRetry=!0),v});f[m].applications.push({applicationId:u.applicationId,date:u.date,result:u.result||"submitted",formPages:u.formPages||null,duration:u.duration||null,retries:u.retries||0,failedField:u.failedField||null,questionCount:u.questionCount||S.length,questions:S})}ht(s,f),console.log(` Phase 2: Rekeyed ${l.applications.length} applications by jobId (${Object.keys(f).length} unique jobs)`),d>0&&console.log(` Phase 2: Stripped bloated options from ${d} questions`)}else l&&!l.applications?console.log(" Phase 2: application-questions.json already in keyed format"):console.log(" Phase 2: No application-questions.json found");if(c.length>0){for(let f of c)delete i[f];ht(n,i),console.log(` Phase 3: Removed ${c.length} merged entries from scouted-jobs.json (${Object.keys(i).length} remaining)`)}else console.log(" Phase 3: No entries to remove from scouted-jobs.json")}var St={};We(St,{NAME:()=>rs,VERSION:()=>ss,up:()=>cs});import*as J from"fs";import*as go from"path";var ss=2,rs="reset-unread-flags";function is(e){return J.existsSync(e)?JSON.parse(J.readFileSync(e,"utf-8")):null}function as(e,t){let o=JSON.stringify(t,null,2),n=e+".tmp";try{J.writeFileSync(n,o)}catch(s){try{J.unlinkSync(n)}catch{}throw s}try{J.renameSync(n,e)}catch(s){try{J.unlinkSync(n)}catch{}if(s.code==="EPERM"||s.code==="EBUSY"){let r=new Error(`File locked: ${e} (${s.code}). On Windows usually antivirus / file explorer / open editor. Close it and re-run.`);throw r.code=s.code,r}throw s}}async function cs(e){let t=go.join(e,"conversations.json"),o=is(t);if(!o){console.log(" conversations.json not found \u2014 nothing to reset.");return}let n=0;for(let s of Object.keys(o))o[s].unread===!0&&(o[s].unread=!1,n++);if(n===0){console.log(" No stale unread flags found \u2014 nothing to do.");return}as(t,o),console.log(` Reset unread=false on ${n} conversations. Run 'ilml linkedin sync-all' to re-establish real unread state from LinkedIn.`)}var wt={};We(wt,{NAME:()=>us,VERSION:()=>ls,up:()=>ms});import*as H from"fs";import*as mo from"path";var ls=3,us="recompute-last-message-by",fs=new Set(["draft","confirmed","rejected","needs_review"]);function ds(e){return H.existsSync(e)?JSON.parse(H.readFileSync(e,"utf-8")):null}function ps(e,t){let o=JSON.stringify(t,null,2),n=e+".tmp";try{H.writeFileSync(n,o)}catch(s){try{H.unlinkSync(n)}catch{}throw s}try{H.renameSync(n,e)}catch(s){try{H.unlinkSync(n)}catch{}if(s.code==="EPERM"||s.code==="EBUSY"){let r=new Error(`File locked: ${e} (${s.code}). On Windows usually antivirus / file explorer / open editor. Close it and re-run.`);throw r.code=s.code,r}throw s}}function gs(e,t){if(!e||e.length===0)return"them";let o=e.filter(r=>!fs.has(r.status));if(o.length===0)return"them";let n=o.map(r=>({m:r,t:Date.parse(r.time)})).filter(r=>Number.isFinite(r.t)).sort((r,i)=>i.t-r.t).map(r=>r.m),s=n.length>0?n:[...o].reverse();for(let r of s){let i=r.sender;if(i===t)return"me";if(i!=="Unknown"&&i!=="")return"them"}return"them"}async function ms(e){let t=mo.join(e,"conversations.json"),o=ds(t);if(!o){console.log(" conversations.json not found \u2014 nothing to recompute.");return}let n=process.env.LINKEDIN_NAME;if(!n||typeof n!="string"||!n.trim())throw new Error(`LINKEDIN_NAME env var is not set. Migration v003 needs your display name to determine which messages are yours vs theirs. Set LINKEDIN_NAME in your .env (or run "ilml plugin config linkedin set LINKEDIN_NAME 'Your Name'") and retry.`);let s=0,r=0,i=0;for(let a of Object.keys(o)){let c=o[a];if(!c||typeof c!="object")continue;Object.prototype.hasOwnProperty.call(c,"_cardSynced")&&(delete c._cardSynced,r++);let l=Array.isArray(c.messages)?c.messages:[],f=gs(l,n),d=f==="me"?"awaiting_reply":"unanswered_by_us";c.lastMessageBy!==f&&i++,c.lastMessageBy=f,c.conversationStatus=d,s++}if(s===0){console.log(" No conversations to recompute.");return}ps(t,o),console.log(` Recomputed lastMessageBy + conversationStatus on ${s} conversations.`),console.log(` ${i} had stale lastMessageBy (now corrected).`),r>0&&console.log(` Cleaned ${r} leftover _cardSynced flags.`),console.log(" Re-run 'ilml linkedin enrich' if you want actionNeeded / summary / autoTags to also update.")}var ys=hs(import.meta.url),Ss=A.dirname(ys),ws=A.dirname(A.dirname(Ss));function bs(e){return e&&(e==="~"?vt.homedir():e.startsWith("~/")||e.startsWith("~\\")?A.join(vt.homedir(),e.slice(2)):e)}var V=process.env.DATA_DIR?A.resolve(bs(process.env.DATA_DIR)):ws,ae=A.join(V,"collected-profiles"),ce=A.join(V,"market-research"),N=A.join(V,".schema-version"),ho=[A.join(ae,".schema-version"),A.join(ce,".schema-version")],te=3,Y=[{version:1,module:yt},{version:2,module:St},{version:3,module:wt}];(function(){Y.sort((n,s)=>n.version-s.version);let t=new Set;for(let n of Y){if(!Number.isInteger(n.version)||n.version<1)throw new Error(`MIGRATION_REGISTRY: invalid version ${JSON.stringify(n.version)} (must be positive integer)`);if(t.has(n.version))throw new Error(`MIGRATION_REGISTRY: duplicate version v${n.version}`);t.add(n.version);let s=n.module;if(!s||typeof s.up!="function")throw new Error(`MIGRATION_REGISTRY: v${n.version} module is missing up()`);if(s.VERSION!==void 0&&s.VERSION!==n.version)throw new Error(`MIGRATION_REGISTRY: v${n.version} entry does not match module VERSION (${s.VERSION}). Drift between registry and module \u2014 pick one source of truth.`)}let o=Y.length?Y[Y.length-1].version:0;if(o!==te)throw new Error(`MIGRATION_REGISTRY: highest version v${o} but CURRENT_SCHEMA_VERSION=${te}. Forgot to bump CURRENT_SCHEMA_VERSION when adding the new migration?`);for(let n=0;n<Y.length;n++){let s=n+1;if(Y[n].version!==s)throw new Error(`MIGRATION_REGISTRY: gap detected \u2014 expected v${s} at position ${n} but found v${Y[n].version}`)}})();function bo(){process.env.DATA_DIR!==void 0&&process.env.DATA_DIR.trim()===""&&process.env.DATA_DIR.length>0&&(console.error(`
|
|
13
13
|
[FATAL] DATA_DIR is set to whitespace ("${process.env.DATA_DIR}"). Either unset it or set a real path.
|
|
14
|
-
`),process.exit(1));try{b.mkdirSync(
|
|
15
|
-
[FATAL] Cannot create DATA_DIR (${
|
|
16
|
-
`),process.exit(1))}let e;try{e=b.statSync(
|
|
17
|
-
[FATAL] DATA_DIR (${
|
|
14
|
+
`),process.exit(1));try{b.mkdirSync(V,{recursive:!0})}catch(t){t.code!=="EEXIST"&&(console.error(`
|
|
15
|
+
[FATAL] Cannot create DATA_DIR (${V}): ${t.message}`),console.error(` Fix the path or permissions and try again.
|
|
16
|
+
`),process.exit(1))}let e;try{e=b.statSync(V)}catch(t){console.error(`
|
|
17
|
+
[FATAL] DATA_DIR (${V}) is not accessible: ${t.message}
|
|
18
18
|
`),process.exit(1)}e.isDirectory()||(console.error(`
|
|
19
|
-
[FATAL] DATA_DIR (${
|
|
20
|
-
`),process.exit(1));for(let t of[
|
|
19
|
+
[FATAL] DATA_DIR (${V}) exists but is NOT a directory.`),console.error(` Looks like a typo \u2014 your DATA_DIR points at a file. Fix the path and try again.
|
|
20
|
+
`),process.exit(1));for(let t of[ae,ce]){let o;try{o=b.lstatSync(t)}catch(n){if(n.code==="ENOENT")continue;throw n}o.isSymbolicLink()&&(console.error(`
|
|
21
21
|
[FATAL] Data subdirectory is a symbolic link: ${t}`),console.error(" Refusing to follow \u2014 backup code would copy whatever the link points at."),console.error(` If this is intentional, replace the symlink with the real directory.
|
|
22
|
-
`),process.exit(1))}}var
|
|
23
|
-
[FATAL] Another plugin process is currently running migrations:`),console.error(` PID: ${t.pid}`),console.error(` Started at: ${t.startedAt||"(unknown)"}`),console.error(` Lock file: ${
|
|
22
|
+
`),process.exit(1))}}var ie=A.join(V,".migration-lock");function vs(){let e=JSON.stringify({pid:process.pid,startedAt:new Date().toISOString()},null,2);try{return b.writeFileSync(ie,e,{flag:"wx"}),!0}catch(n){if(n.code!=="EEXIST")throw n}let t;try{t=JSON.parse(b.readFileSync(ie,"utf-8"))}catch{t=null}if(t&&Number.isInteger(t.pid)){let n=!1;try{process.kill(t.pid,0),n=!0}catch(i){i.code==="EPERM"&&(n=!0)}let s=24*60*60*1e3,r=1/0;if(t.startedAt){let i=Date.parse(t.startedAt);Number.isNaN(i)||(r=Date.now()-i)}n&&r<s&&(console.error(`
|
|
23
|
+
[FATAL] Another plugin process is currently running migrations:`),console.error(` PID: ${t.pid}`),console.error(` Started at: ${t.startedAt||"(unknown)"}`),console.error(` Lock file: ${ie}
|
|
24
24
|
`),console.error(" Wait for it to finish, or if you're sure it's not actually running,"),console.error(` delete the lock file manually and re-run.
|
|
25
|
-
`),process.exit(1)),n&&r>=s&&console.log(` Lock holder PID ${t.pid} is alive but lock is older than 24h \u2014 assuming PID reuse, taking over.`)}console.log(` Stale migration lock from PID ${t?.pid??"?"} found \u2014 taking over.`);let o=
|
|
25
|
+
`),process.exit(1)),n&&r>=s&&console.log(` Lock holder PID ${t.pid} is alive but lock is older than 24h \u2014 assuming PID reuse, taking over.`)}console.log(` Stale migration lock from PID ${t?.pid??"?"} found \u2014 taking over.`);let o=ie+".tmp";try{return b.writeFileSync(o,e),b.renameSync(o,ie),!0}catch(n){try{b.unlinkSync(o)}catch{}throw new Error(`Failed to take over stale migration lock: ${n.message}`)}}function $t(){try{if(JSON.parse(b.readFileSync(ie,"utf-8")).pid!==process.pid)return;b.unlinkSync(ie)}catch{}}var yo=!1;function $s(){if(yo)return;yo=!0;let e=()=>{try{$t()}catch{}process.exit(1)};for(let t of["SIGINT","SIGTERM","SIGHUP"])try{process.on(t,e)}catch{}}function ks(){if(b.existsSync(N))return;let e=[],t=[];for(let r of ho)if(b.existsSync(r))try{e.push({file:r,schema:qe(r)})}catch(i){t.push({file:r,err:i.message})}if(t.length>0){console.error(`
|
|
26
26
|
[FATAL] Legacy .schema-version file(s) corrupted \u2014 cannot determine current schema version:`);for(let r of t)console.error(` ${r.file}: ${r.err}`);console.error(`
|
|
27
27
|
Repair the file(s) (likely just an integer 'version' field \u2014 see another working DATA_DIR`),console.error(` or default to {"version":1,"migrations":[]} if you're sure you're on schema v1) and try again.
|
|
28
28
|
`),process.exit(1)}if(e.length===0)return;let o=e.map(r=>r.schema.version);if(new Set(o).size>1){console.error(`
|
|
29
29
|
[FATAL] Legacy .schema-version files disagree:`);for(let r of e)console.error(` ${r.file}: v${r.schema.version}`);console.error(" This means a previous migration crashed between writing the two files."),console.error(` Reconcile manually (delete the wrong one, keep the right one), then re-run.
|
|
30
|
-
`),process.exit(1)}let n=e[0].schema,s=
|
|
31
|
-
[FATAL] ${
|
|
32
|
-
`),process.exit(1)}let t=
|
|
33
|
-
[FATAL] ${
|
|
34
|
-
`),process.exit(1)),t}function
|
|
35
|
-
[FATAL] Your data schema is at v${t}, but this plugin build only understands up to v${
|
|
36
|
-
`),console.error(" Options:"),console.error(" A) Re-install the newer plugin version that produced this schema, then"),console.error(" (if you really want to downgrade) run 'rollback-data --confirm' first,"),console.error(" then re-install the older plugin version."),console.error(` B) Manually restore your data from the *.pre-vNNN.backup files matching v${
|
|
30
|
+
`),process.exit(1)}let n=e[0].schema,s=N+".tmp";try{b.writeFileSync(s,JSON.stringify(n,null,2)),b.renameSync(s,N)}catch(r){try{b.unlinkSync(s)}catch{}throw r}for(let r of ho)if(b.existsSync(r))try{b.unlinkSync(r)}catch{}console.log(` Consolidated legacy .schema-version files \u2192 ${N}`)}function Cs(e){if(e===null||typeof e!="object"||Array.isArray(e))return null;let t=Number(e.version);return!Number.isFinite(t)||t<0||!Number.isInteger(t)?null:(e.version=t,Array.isArray(e.migrations)||(e.migrations=[]),e)}function qe(e){let t=b.readFileSync(e,"utf-8");return t.charCodeAt(0)===65279&&(t=t.slice(1)),JSON.parse(t)}function So(){if(ks(),!b.existsSync(N))return{version:0,migrations:[]};let e;try{e=qe(N)}catch(o){if(o.code==="ENOENT")return{version:0,migrations:[]};console.error(`
|
|
31
|
+
[FATAL] ${N} is not valid JSON: ${o.message}`),console.error(" Refusing to proceed \u2014 silently treating it as 'fresh install' would re-apply"),console.error(" every migration on top of already-migrated data and corrupt it."),console.error(' Repair the file (set it to {"version":N,"migrations":[...]} matching your real state)'),console.error(` or restore from the *.pre-vNNN.backup files if you have them.
|
|
32
|
+
`),process.exit(1)}let t=Cs(e);return t===null&&(console.error(`
|
|
33
|
+
[FATAL] ${N} has invalid structure:`),console.error(` ${JSON.stringify(e)?.slice(0,200)||"(unparseable)"}`),console.error(' Expected shape: { "version": <integer>, "migrations": [...] }'),console.error(` Repair the file or restore from a backup.
|
|
34
|
+
`),process.exit(1)),t}function vo(e){try{b.chmodSync(e,384)}catch{}}function Ts(e){if(bo(),b.existsSync(N))try{if(b.lstatSync(N).isSymbolicLink())throw new Error(`SCHEMA_FILE is a symbolic link: ${N}. Refusing to write through it (potential symlink attack). Remove the link and re-run.`)}catch(o){if(o.message?.includes("symbolic link"))throw o}let t=N+".tmp";try{b.writeFileSync(t,JSON.stringify(e,null,2)),vo(t),b.renameSync(t,N)}catch(o){try{b.unlinkSync(t)}catch{}throw o}}function wo(e,t){if(!b.existsSync(e))return;let o=`.pre-v${String(t).padStart(3,"0")}.backup`,n=b.readdirSync(e).filter(a=>{let c=a.toLowerCase();if(c.includes(".backup")||c===".schema-version"||c.endsWith(".tmp"))return!1;try{return b.statSync(A.join(e,a)).isFile()}catch{return!1}}),s=0,r=0,i=[];for(let a of n){let c=A.join(e,a),l=A.join(e,a+o);if(b.existsSync(l)){r++;continue}try{ko(c,l),s++}catch(f){i.push({file:a,err:f.message})}}if(i.length>0){let a=new Error(`Failed to back up ${i.length} file(s) in ${A.basename(e)}/: `+i.map(c=>`${c.file} (${c.err})`).join(", "));throw a.code="BACKUP_INCOMPLETE",a}if(s>0||r>0){let a=r>0?` (${r} pre-existing backup(s) preserved)`:"";console.log(` Backed up ${s} files in ${A.basename(e)}/ (${o})${a}`)}}var bt=!1;async function $o(){if(bt)return;bo(),Ds();let t=So().version;if(t===te){bt=!0;return}$s(),vs(),process.on("exit",$t),t>te&&(console.error(`
|
|
35
|
+
[FATAL] Your data schema is at v${t}, but this plugin build only understands up to v${te}.`),console.error(" This usually means you downgraded the plugin without rolling back data."),console.error(` Operating on the newer-format data with this build would risk corrupting it.
|
|
36
|
+
`),console.error(" Options:"),console.error(" A) Re-install the newer plugin version that produced this schema, then"),console.error(" (if you really want to downgrade) run 'rollback-data --confirm' first,"),console.error(" then re-install the older plugin version."),console.error(` B) Manually restore your data from the *.pre-vNNN.backup files matching v${te}.
|
|
37
37
|
`),process.exit(1)),console.log(`
|
|
38
|
-
\u{1F4E6} Schema migration: v${t} \u2192 v${
|
|
38
|
+
\u{1F4E6} Schema migration: v${t} \u2192 v${te}`);let o=Os();if(!o.ok){console.error(`
|
|
39
39
|
[FATAL] Cannot migrate \u2014 current data is corrupted:`);for(let s of o.broken)console.error(` ${s.dir}/${s.file}: ${s.err}`);console.error(`
|
|
40
40
|
Migration ABORTED. No backups taken, no files modified.`),console.error(" Repair the file(s) above (restore from your own backup, or fix manually)"),console.error(` and try again. To inspect: 'ilml linkedin migrate-status'.
|
|
41
|
-
`),process.exit(1)}let n=
|
|
42
|
-
Running migration v${String(s.version).padStart(3,"0")}...`);let r=`v${String(s.version).padStart(3,"0")}`,i=`.pre-${r}.backup`;try{
|
|
43
|
-
[FATAL] Pre-migration backup failed for ${r}: ${
|
|
44
|
-
`),process.exit(1)}let a=s.module;try{await a.up(
|
|
45
|
-
[FATAL] Migration ${r} failed: ${
|
|
46
|
-
`)):(console.error(` [!] Auto-rollback INCOMPLETE: ${
|
|
47
|
-
`))}catch(
|
|
48
|
-
`)}process.exit(1)}try{let
|
|
49
|
-
[FATAL] Migration ${r} applied successfully but schema version file could NOT be updated: ${
|
|
50
|
-
`),process.exit(1)}console.log(` \u2713 Migration ${r} complete`)}try{
|
|
51
|
-
\u2713 Schema is now at v${
|
|
52
|
-
`)}var
|
|
53
|
-
\u{1F504} CDP dead \u2014 recovering page (#${
|
|
54
|
-
`)}function
|
|
41
|
+
`),process.exit(1)}let n=Y.filter(s=>s.version>t);for(let s of n){console.log(`
|
|
42
|
+
Running migration v${String(s.version).padStart(3,"0")}...`);let r=`v${String(s.version).padStart(3,"0")}`,i=`.pre-${r}.backup`;try{wo(ae,s.version),wo(ce,s.version)}catch(c){console.error(`
|
|
43
|
+
[FATAL] Pre-migration backup failed for ${r}: ${c.message}`),console.error(" Migration NOT applied. Repair the underlying issue (disk space, permissions, locked files)"),console.error(` and re-run the plugin command.
|
|
44
|
+
`),process.exit(1)}let a=s.module;try{await a.up(ae,ce)}catch(c){console.error(`
|
|
45
|
+
[FATAL] Migration ${r} failed: ${c.message}`),console.error(` Attempting auto-rollback from ${i} files...`);try{let l=Is(i);l.ok?(console.error(` \u2713 Auto-rollback restored ${l.restored} files. Schema stays at v${t}.`),console.error(` The migration will be retried on next plugin command. Fix the root cause first.
|
|
46
|
+
`)):(console.error(` [!] Auto-rollback INCOMPLETE: ${l.error}`),console.error(` Manual recovery: copy *${i} files back over the originals.
|
|
47
|
+
`))}catch(l){console.error(` [!] Auto-rollback CRASHED while restoring: ${l.message}`),console.error(` Manual recovery: copy *${i} files back over the originals.
|
|
48
|
+
`)}process.exit(1)}try{let c=new Date().toISOString(),l={version:s.version,name:a.NAME||r,appliedAt:c},f=So();f.version=s.version,f.migratedAt=c,f.migrations=f.migrations||[],f.migrations.push(l),Ts(f)}catch(c){console.error(`
|
|
49
|
+
[FATAL] Migration ${r} applied successfully but schema version file could NOT be updated: ${c.message}`),console.error(` Your data is in the v${s.version} state but .schema-version still shows v${t}.`),console.error(` On next plugin command the runner will try to re-apply ${r}. If that migration is`),console.error(` idempotent (most are), it will succeed harmlessly. If you're unsure, run 'data-cleanup' and contact support.
|
|
50
|
+
`),process.exit(1)}console.log(` \u2713 Migration ${r} complete`)}try{Ft()}catch{}try{Vt()}catch{}bt=!0,$t(),console.log(`
|
|
51
|
+
\u2713 Schema is now at v${te}
|
|
52
|
+
`)}var Es=[/\.json\.tmp$/,/\.schema-version\.tmp$/,/\.backup\.tmp$/];function _s(e){let t=e.toLowerCase();return Es.some(o=>o.test(t))}var As=6e4;function Rs(){let e=Date.now(),t=[];for(let o of[V,ae,ce]){if(!b.existsSync(o))continue;let n;try{n=b.readdirSync(o)}catch{continue}for(let s of n){if(!_s(s))continue;let r=A.join(o,s);try{let i=b.statSync(r);if(!i.isFile()||e-i.mtimeMs<As)continue;t.push({dir:o,file:s,fullPath:r})}catch{}}}return t}function Ds({verbose:e=!1}={}){let t=Rs();if(t.length===0)return e&&console.log(" No orphan .tmp files found."),0;let o=0;for(let n of t)try{b.unlinkSync(n.fullPath),o++,e&&console.log(` Removed orphan: ${A.basename(n.dir)}/${n.file}`)}catch(s){console.warn(` [!] Could not remove orphan ${n.fullPath}: ${s.message}`)}return!e&&o>0&&console.log(` Cleaned up ${o} orphan .tmp file(s) from previous run.`),o}function Os(){let e=[];for(let t of[ae,ce]){if(!b.existsSync(t))continue;let o=b.readdirSync(t).filter(n=>{let s=n.toLowerCase();return s.endsWith(".json")&&!s.includes(".backup")});for(let n of o){let s=A.join(t,n);try{qe(s)}catch(r){e.push({dir:A.basename(t),file:n,err:r.message})}}}return e.length===0?{ok:!0}:{ok:!1,broken:e}}function Is(e){let t=[],o=[];for(let r of[ae,ce]){if(!b.existsSync(r))continue;let i=b.readdirSync(r).filter(a=>a.endsWith(e));for(let a of i){let c=A.join(r,a),l=a.slice(0,-e.length),f=A.join(r,l);try{l.toLowerCase().endsWith(".json")?qe(c):b.accessSync(c,b.constants.R_OK),t.push({backupPath:c,originalPath:f,fileName:a,dir:r})}catch(d){o.push(`${A.basename(r)}/${a}: ${d.message}`)}}}if(o.length>0)return{ok:!1,error:o.join("; "),restored:0};let n=0,s=[];for(let{backupPath:r,originalPath:i,fileName:a,dir:c}of t)try{ko(r,i),n++}catch(l){s.push(`${A.basename(c)}/${a}: ${l.message}`)}return s.length>0?{ok:!1,error:s.join("; "),restored:n}:{ok:!0,restored:n}}function ko(e,t){let o=t+".tmp";try{b.copyFileSync(e,o),vo(o)}catch(n){try{b.unlinkSync(o)}catch{}throw n}try{b.renameSync(o,t)}catch(n){try{b.unlinkSync(o)}catch{}if(n.code==="EPERM"||n.code==="EBUSY"){let s=new Error(`File locked: ${t} (${n.code}). On Windows this is usually antivirus / Search Indexer / the file open in another app. Close anything that might be holding it and re-run.`);throw s.code=n.code,s}throw n}}async function xs(e,t,o="op"){let n,s=new Promise((r,i)=>{n=setTimeout(()=>i(new Error(`${o} timeout after ${t}ms`)),t)});try{return await Promise.race([e,s])}finally{clearTimeout(n)}}function le(e){if(!e)return!1;let t=e.constructor?.name||"";if(t==="ProtocolError"||t==="TargetCloseError")return!0;let o=e.message||"";return/Runtime\.callFunctionOn timed out/i.test(o)||/Target closed/i.test(o)||/Session closed/i.test(o)||/Connection closed/i.test(o)||/Most likely the page has been closed/i.test(o)||/Navigating frame was detached/i.test(o)||/Protocol error.*timed out/i.test(o)}async function Co(e,t,{returnUrl:o,gotoTimeoutMs:n=6e4,cookiesProvider:s=null,setupPage:r=null}={}){try{await xs(t.close({runBeforeUnload:!1}),5e3,"oldPage.close")}catch{}let i=await e.newPage();if(r)try{await r(i)}catch{}if(s)try{let a=s();Array.isArray(a)&&a.length>0&&await i.setCookie(...a)}catch{}if(o){await i.goto(o,{waitUntil:"domcontentloaded",timeout:n});let a=i.url();if(/\/login|\/checkpoint|\/uas\/login|\/authwall/.test(a))throw new Error(`Session expired after recovery \u2014 landed on ${a}. Run: ilml linkedin login`)}return i}await $o();var Ro="https://www.linkedin.com/messaging/",js=10,kt=3,Ns=async e=>{await e.evaluateOnNewDocument(()=>{Object.defineProperty(navigator,"webdriver",{get:()=>{}})})},Ms=()=>Pe(),ue={pageRecoveries:0};async function Je(e,t){if(ue.pageRecoveries>=js)throw new Error(`CDP unrecoverable: ${ue.pageRecoveries} page recoveries already attempted. Last: ${t}`);console.log(`
|
|
53
|
+
\u{1F504} CDP dead \u2014 recovering page (#${ue.pageRecoveries+1}). Reason: ${t}`),e.page=await Co(e.browser,e.page,{returnUrl:Ro,cookiesProvider:Ms,setupPage:Ns}),ue.pageRecoveries++,await _(3e3);try{await e.page.waitForSelector(".msg-conversation-listitem",{timeout:15e3})}catch{}console.log(`\u2713 Page recovered (total: ${ue.pageRecoveries}).
|
|
54
|
+
`)}function ye(e){return(e.messages||[]).find(t=>$e.has(t.status))?.status}var z=process.argv.slice(2),Ct=z.includes("--full"),L=z.includes("--dry-run"),To=z.includes("--report"),Tt=z.includes("--verbose"),Eo=z.includes("--backfill"),_o=z.includes("--enrich"),Ao=z.includes("--today"),Et=z.includes("--read-unread"),Oe=parseInt(z.find(e=>e.startsWith("--max="))?.split("=")[1]||"50");function Ps(e,t){if(!t)return"new";if(e.unread)return"unread";if(!t.profileUrl&&(t.messages?.length||t.lastMessagePreview))return"missing_url";let o=(t.lastMessagePreview||"").substring(0,50),n=(e.lastMessagePreview||"").substring(0,50);if(n&&o&&n!==o)return"preview_changed";if(e.lastMessageBy==="me"&&t.lastMessageBy!=="me")return"sender_changed";if(t.lastSynced&&e.lastMessageTime){let s=new Date(e.lastMessageTime).getTime(),r=new Date(t.lastSynced).getTime();if(Number.isFinite(s)&&Number.isFinite(r)&&s>r)return"activity_after_last_read"}return null}async function Fs(e,t){console.log(`
|
|
55
55
|
\u2550\u2550 PHASE 1: Inbox Scan \u2550\u2550
|
|
56
|
-
`);let o=
|
|
57
|
-
3 rounds with no new items \u2014 end of visible list.`);break}}else d=0;if(!
|
|
58
|
-
Caught up \u2014 3 screens with no new threads to read.`);break}}else
|
|
59
|
-
Scroll stalled \u2014 reached end of inbox.`);break}}else m=0;u=g.scrollTop}else{let p=await e.$(".msg-conversation-listitem");if(p){let w=await p.boundingBox();w&&(await e.mouse.move(w.x+w.width/2,w.y+w.height/2),await e.mouse.wheel({deltaY:600}))}}await
|
|
60
|
-
`);let S=new Date().toISOString(),y=0;for(let[$,{card:
|
|
61
|
-
Inbox scan complete:`),console.log(` Total conversations seen: ${s.size}`),console.log(` New conversations: ${y}`),console.log(` Need reading/re-reading: ${r.size}`)
|
|
56
|
+
`);let o=Ze(),n=o.lastIncrementalSync?new Date(o.lastIncrementalSync):null,s=new Map,r=new Map,i=new Map,a=0,c=0,l=0,f=0,d=0,u=-1,m=0;for(;;){c++;let $=await to(e),k=0;for(let p of $){if(!p.name||s.has(p.name))continue;s.set(p.name,p),k++,p.unread&&i.set(p.name,p);let w=t[p.name],C=Ps(p,w);C&&(!Et&&p.unread?a++:r.set(p.name,{card:p,reason:C})),w&&(C||(w.lastMessageBy=p.lastMessageBy,w.lastMessagePreview=p.lastMessagePreview||w.lastMessagePreview,w.lastMessageTime=p.lastMessageTime||w.lastMessageTime),w.unread=p.unread,p.lastMessageBy==="them"&&p.unread&&(w.conversationStatus="unanswered_by_us"))}if(Tt?console.log(` Round #${c}: +${k} new | Total: ${s.size} | Need reading: ${r.size}`):process.stdout.write(`\r Scanned: ${s.size} conversations (page #${c}, ${r.size} need reading)`),k===0){if(d++,d>=3){console.log(`
|
|
57
|
+
3 rounds with no new items \u2014 end of visible list.`);break}}else d=0;if(!Ct){if(r.size===f){if(l++,l>=3){console.log(`
|
|
58
|
+
Caught up \u2014 3 screens with no new threads to read.`);break}}else l=0;f=r.size}let g=await nt(e);if(g){if(g.scrollTop===u){if(m++,m>=3){console.log(`
|
|
59
|
+
Scroll stalled \u2014 reached end of inbox.`);break}}else m=0;u=g.scrollTop}else{let p=await e.$(".msg-conversation-listitem");if(p){let w=await p.boundingBox();w&&(await e.mouse.move(w.x+w.width/2,w.y+w.height/2),await e.mouse.wheel({deltaY:600}))}}await _(2e3)}Tt||process.stdout.write(`
|
|
60
|
+
`);let S=new Date().toISOString(),y=0;for(let[$,{card:k}]of r)t[$]||(t[$]={profileUrl:null,lastSynced:S,lastMessageBy:k.lastMessageBy,lastMessagePreview:k.lastMessagePreview||"",lastMessageTime:k.lastMessageTime||"",unread:k.unread||!1,conversationStatus:k.lastMessageBy==="them"?"unanswered_by_us":"awaiting_reply",messages:[]},y++);let v=0;for(let[$,k]of i)t[$]||(t[$]={profileUrl:null,lastSynced:S,lastMessageBy:k.lastMessageBy,lastMessagePreview:k.lastMessagePreview||"",lastMessageTime:k.lastMessageTime||"",unread:!0,conversationStatus:k.lastMessageBy==="them"?"unanswered_by_us":"awaiting_reply",messages:[]},v++);if(console.log(`
|
|
61
|
+
Inbox scan complete:`),console.log(` Total conversations seen: ${s.size}`),console.log(` New conversations: ${y}`),console.log(` Need reading/re-reading: ${r.size}`),!Et&&a>0&&console.log(` Preserved unread (default): ${a} \u2014 kept bold in LinkedIn UI; pass --read-unread to vacuum them into the DB`),v>0&&console.log(` New unread stubs: ${v} \u2014 saved to DB so 'today' / 'report' can show them offline`),r.size>0){let $={};for(let{reason:k}of r.values())$[k]=($[k]||0)+1;for(let[k,g]of Object.entries($))console.log(` ${k}: ${g}`)}if(!L){let $=Ze();$.lastIncrementalSync=new Date().toISOString(),Ct&&($.lastFullSync=$.lastIncrementalSync),Jt($),X(t)}return{allCards:s,toReRead:r,unreadCards:i}}async function Ls(e,t,o,n){console.log(`
|
|
62
62
|
\u2550\u2550 PHASE 2: Read Changed Threads \u2550\u2550
|
|
63
|
-
`);let s=Array.from(t.entries()),r=Math.min(s.length,
|
|
64
|
-
`);let a=[],
|
|
65
|
-
Phase 2 complete: ${a.length} read, ${
|
|
63
|
+
`);let s=Array.from(t.entries()),r=Math.min(s.length,Oe);if(r===0)return console.log(" No threads to read \u2014 all up to date."),[];console.log(" Resetting inbox view...");try{await e.page.goto(Ro,{waitUntil:"domcontentloaded",timeout:6e4})}catch(d){if(!le(d))throw d;await Je(e,"Phase 2 inbox reset")}await _(4e3);try{await e.page.waitForSelector(".msg-conversation-listitem",{timeout:15e3})}catch{}let i={unread:0,preview_changed:1,sender_changed:2,no_messages:3,new:4};s.sort((d,u)=>(i[d[1].reason]??5)-(i[u[1].reason]??5)),console.log(` Reading ${r} of ${s.length} threads (--max=${Oe})
|
|
64
|
+
`);let a=[],c=[],l=0,f=0;for(let d=0;d<r;d++){let[u,{card:m,reason:S}]=s[d];console.log(` [${d+1}/${r}] ${u} (${S})`);let y=e.page;try{let v={verified:!1,headerName:null};try{v=await ct(y,u)}catch(k){if(le(k))throw k;console.log(` searchAndOpenThread threw: ${(k.message||"").substring(0,80)}`)}if(!v.verified){console.log(" Sidebar fallback (scroll-to-find)...");try{v=await oo(e.page,u)}catch(k){if(le(k))throw k;console.log(` Fallback threw: ${(k.message||"").substring(0,80)}`)}}if(!v.verified){console.log(" Could not open thread."),l++;try{await Z(e.page)}catch{}continue}let $=await rt(y);if($&&$.length>0){if(!it($,u,c)){console.log(" Skipped \u2014 stale data."),l++,await Z(y);continue}let g=await st(y),p=o[u]||{profileUrl:null,lastSynced:new Date().toISOString(),lastMessageBy:null,lastMessagePreview:"",lastMessageTime:"",unread:!1,conversationStatus:null,messages:[]},w=p.messages?.length||0;p.messages=at(p.messages,$),p.lastSynced=new Date().toISOString(),g&&!p.profileUrl&&(p.profileUrl=g);let C=p.messages.filter(I=>!["draft","confirmed","rejected","needs_review"].includes(I.status));p.lastMessageBy=ft(C,u),p.lastMessagePreview=m.lastMessagePreview||p.lastMessagePreview,p.lastMessageTime=m.lastMessageTime||p.lastMessageTime,p.unread=!1,o[u]=p,L||X(o);let R=p.messages.length-w;console.log(` ${p.messages.length} msgs (${R>=0?"+"+R:R} new)${g&&!p.profileUrl?" +url":""}`),a.push({name:u}),c=[...new Set($.map(I=>I.sender).filter(I=>I&&I!=="Unknown"))]}else console.log(" No messages extracted."),l++;await Z(y),await _(500)}catch(v){if(!le(v))throw v;f++,n&&n.add(u),await Je(e,`Phase 2 thread ${d+1}/${r} (${u})`)}}return console.log(`
|
|
65
|
+
Phase 2 complete: ${a.length} read, ${l} failed${f?`, ${f} skipped on CDP recovery`:""}`),s.length>r&&console.log(` ${s.length-r} more threads pending (run again or increase --max)`),a}function Us(e,t){console.log(`
|
|
66
66
|
\u2550\u2550 PHASE 3: Post-processing & Enrichment \u2550\u2550
|
|
67
|
-
`);let o=
|
|
67
|
+
`);let o=W();for(let n of t){let{name:s}=n,r=e[s];if(!r?.messages?.length)continue;try{ut(r,s,o)}catch(c){console.warn(` [!] Enrichment failed for ${s}: ${c.message}`)}r._cardSynced&&delete r._cardSynced;let i=r.profileUrl,a=null;if(i&&(a=o[i]||null),!a){let c=Object.entries(o).filter(([,l])=>l.name===s);c.length===1&&(i||(i=c[0][0],r.profileUrl=i),a=c[0][1])}!a&&i&&(Bt(i,{name:s,source:"sync-all"}),a=W()[i]),a&&(r.category&&!a.category&&(a.category=r.category),a.conversationStatus=r.conversationStatus,a.lastUpdated=new Date().toISOString()),i&&a&&(r.isConnection=a.connectionStatus==="connected")}L||(Ne(o),X(e)),console.log(` Enriched ${t.length} conversations.`),Do(e)}function Do(e){let t=new Map;for(let[s,r]of Object.entries(e)){if(!r.profileUrl)continue;let i=r.profileUrl.toLowerCase().replace(/\/$/,"");t.has(i)||t.set(i,[]),t.get(i).push(s)}let o=[...t.entries()].filter(([,s])=>s.length===2);if(o.length===0)return;let n=0;for(let[,s]of o){let[r,i]=s.map(f=>({name:f,conv:e[f]})),a=(r.conv.messages?.length||0)>=(i.conv.messages?.length||0)?r:i,c=a===r?i:r,l=new Set((a.conv.messages||[]).map(f=>`${f.text?.slice(0,80)}|${f.time}`));for(let f of c.conv.messages||[]){let d=`${f.text?.slice(0,80)}|${f.time}`;l.has(d)||(a.conv.messages.push(f),l.add(d))}a.conv.messages.sort((f,d)=>(f.time||"").localeCompare(d.time||"")),c.conv.notes&&!a.conv.notes&&(a.conv.notes=c.conv.notes),c.conv.priority&&!a.conv.priority&&(a.conv.priority=c.conv.priority),c.conv.categorySource==="manual"&&a.conv.categorySource!=="manual"&&(a.conv.category=c.conv.category,a.conv.categorySource="manual"),(c.conv.lastSynced||"")>(a.conv.lastSynced||"")&&(a.conv.lastSynced=c.conv.lastSynced),delete e[c.name],console.log(` Merged: "${c.name}" \u2192 "${a.name}"`),n++}n>0&&(L||X(e),console.log(` ${n} duplicates merged.`))}function Bs(){let e=Te(),t=W(),o=Object.entries(e);console.log(`
|
|
68
68
|
\u2550\u2550 ENRICHMENT: Re-processing all conversations \u2550\u2550
|
|
69
|
-
`);let n=0,s=0,r=0;for(let[d,u]of o){if(!u.profileUrl){let m=Object.entries(t).filter(([,S])=>S.name===d);m.length===1&&(u.profileUrl=m[0][0],r++)}if(!u.messages||u.messages.length===0){s++;continue}try{
|
|
70
|
-
People enriched: ${
|
|
69
|
+
`);let n=0,s=0,r=0;for(let[d,u]of o){if(!u.profileUrl){let m=Object.entries(t).filter(([,S])=>S.name===d);m.length===1&&(u.profileUrl=m[0][0],r++)}if(!u.messages||u.messages.length===0){s++;continue}try{ut(u,d,t)}catch(m){console.warn(` [!] Enrichment failed for ${d}: ${m.message}`)}n++}Do(e),L||X(e),console.log(` Enriched: ${n}`),console.log(` Skipped (no messages): ${s}`),r>0&&console.log(` Linked profileUrl from people.json: ${r}`),console.log(` Total: ${o.length}`);let i=new Set(Object.keys(e)),a=new Set(Object.values(e).map(d=>d.profileUrl).filter(Boolean)),c=Object.entries(t),l=0,f=0;for(let[d,u]of c){if(a.has(d)){f++;continue}if(u.name&&i.has(u.name)){f++;continue}if(u.connectionStatus!=="connected"){f++;continue}ao(u,d),l++}L||Ne(t),console.log(`
|
|
70
|
+
People enriched: ${l}`),console.log(` People skipped (have conversation or not connected): ${f}`),console.log(` People total: ${c.length}`),_t(e,null,null)}function _t(e,t,o,n={}){let s=Object.entries(e),r=W(),{unreadCards:i=null,readUnread:a=!1}=n;console.log(`
|
|
71
71
|
\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550`),console.log(" SYNC REPORT"),console.log(`\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
|
|
72
|
-
`),t&&(console.log(" Inbox Scan:"),console.log(` Conversations seen: ${t.allCards.size}`),console.log(` Needed reading: ${t.toReRead.size}`)),o&&console.log(` Actually read: ${o.length}`);let
|
|
73
|
-
Database:`),console.log(` Total conversations: ${
|
|
72
|
+
`),t&&(console.log(" Inbox Scan:"),console.log(` Conversations seen: ${t.allCards.size}`),console.log(` Needed reading: ${t.toReRead.size}`)),o&&console.log(` Actually read: ${o.length}`);let c=s.length,l=s.filter(([,h])=>h.messages?.length>0).length,f=s.filter(([,h])=>!h.messages||h.messages.length===0).length,d=s.filter(([,h])=>h.profileUrl).length,u=s.filter(([,h])=>!h.profileUrl).length;console.log(`
|
|
73
|
+
Database:`),console.log(` Total conversations: ${c}`),console.log(` With messages: ${l}`),console.log(` Without messages: ${f}`),console.log(` With profileUrl: ${d}`),console.log(` Without profileUrl: ${u}`);let m=0,S=0;for(let[,h]of s)for(let T of h.messages||[])m++,(!T.sender||T.sender==="Unknown")&&S++;console.log(`
|
|
74
74
|
Message Quality:`),console.log(` Total messages: ${m}`),console.log(` Unknown sender: ${S} (${m>0?(S/m*100).toFixed(1):0}%)`);let y={};for(let[,h]of s){let T=h.conversationStatus||"null";y[T]=(y[T]||0)+1}console.log(`
|
|
75
|
-
Statuses:`);for(let[h,T]of Object.entries(y).sort((
|
|
76
|
-
Categories:`);for(let[h,T]of Object.entries(v).sort((
|
|
77
|
-
Actions Needed:`);for(let[h,T]of Object.entries($).sort((
|
|
78
|
-
Duplicates (${g.length}):`);for(let[h,T]of g)console.log(` ${T.join(" | ")} \u2192 ${h}`)}let p=s.filter(([,h])=>h.conversationStatus==="unanswered_by_us").sort((h,T)=>(T[1].lastSynced||"").localeCompare(h[1].lastSynced||"")),w=s.filter(([,h])=>
|
|
75
|
+
Statuses:`);for(let[h,T]of Object.entries(y).sort((E,D)=>D[1]-E[1]))console.log(` ${h}: ${T}`);let v={};for(let[,h]of s){let T=h.category||"(none)";v[T]=(v[T]||0)+1}console.log(`
|
|
76
|
+
Categories:`);for(let[h,T]of Object.entries(v).sort((E,D)=>D[1]-E[1]))console.log(` ${h}: ${T}`);let $={};for(let[,h]of s){let T=h.actionNeeded||"(none)";$[T]=($[T]||0)+1}if(Object.keys($).length>1||!$["(none)"]){console.log(`
|
|
77
|
+
Actions Needed:`);for(let[h,T]of Object.entries($).sort((E,D)=>D[1]-E[1]))console.log(` ${h}: ${T}`)}let k=new Map;for(let[h,T]of s){if(!T.profileUrl)continue;let E=T.profileUrl.toLowerCase().replace(/\/$/,"");k.has(E)||k.set(E,[]),k.get(E).push(h)}let g=[...k.entries()].filter(([,h])=>h.length>1);if(g.length>0){console.log(`
|
|
78
|
+
Duplicates (${g.length}):`);for(let[h,T]of g)console.log(` ${T.join(" | ")} \u2192 ${h}`)}let p=s.filter(([,h])=>h.conversationStatus==="unanswered_by_us").sort((h,T)=>(T[1].lastSynced||"").localeCompare(h[1].lastSynced||"")),w=s.filter(([,h])=>ye(h));if(console.log(`
|
|
79
79
|
\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500`),console.log(" ACTION NEEDED"),console.log(`\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
80
80
|
`),i&&!a&&i.size>0){let h=[...i.entries()];console.log(` STILL UNREAD ON LINKEDIN (${h.length}) \u2014 kept bold; read them in the LinkedIn UI:
|
|
81
|
-
`);let T=30;for(let[
|
|
81
|
+
`);let T=30;for(let[E,D]of h.slice(0,T)){let M=e[E],Ie=M?.category?` [${M.category}]`:"",we=D.lastMessageTime||M?.lastMessageTime||"",Ve=(D.lastMessagePreview||"").slice(0,80),xe=M?.summary,be=M?.personContext?.title?`${M.personContext.title}${M.personContext.company?` at ${M.personContext.company}`:""}`:null;console.log(` ${E}${Ie}${we?` \u2014 ${we}`:""}`),Ve&&console.log(` "${Ve}"`),xe?console.log(` ${xe}`):be&&console.log(` ${be}`)}h.length>T&&console.log(`
|
|
82
82
|
... and ${h.length-T} more`),console.log(`
|
|
83
83
|
To pull them into the local DB (will lose unread badges): npm run sync-all -- --read-unread
|
|
84
|
-
`)}if(w.length>0){let h=w.filter(([,
|
|
85
|
-
`);for(let[h,T]of
|
|
86
|
-
`);let h=20;for(let[T,
|
|
87
|
-
... and ${R.length-h} more`)}p.length===0&&console.log(" All caught up \u2014 no unanswered messages.");let
|
|
88
|
-
INITIATE CONTACT (${
|
|
89
|
-
`);let h=20;for(let[T,
|
|
90
|
-
... and ${
|
|
91
|
-
BALL IN THEIR COURT: ${
|
|
84
|
+
`)}if(w.length>0){let h=w.filter(([,E])=>ye(E)===j.CONFIRMED),T=w.filter(([,E])=>{let D=ye(E);return D===j.DRAFT||D===j.NEEDS_REVIEW});console.log(` Drafts: ${w.length} total (${h.length} confirmed, ${T.length} pending)`),T.length>0&&console.log(" Review: npm run messages -- --review-drafts"),h.length>0&&console.log(" Send: npm run messages -- --push-drafts"),console.log()}let C=p.filter(([,h])=>h.actionNeeded==="schedule_call"),R=p.filter(([,h])=>h.actionNeeded!=="schedule_call");if(R.sort((h,T)=>{let E={high:0,medium:1,low:2}[h[1].suggestedPriority]??3,D={high:0,medium:1,low:2}[T[1].suggestedPriority]??3;return E-D}),C.length>0){console.log(` SCHEDULE CALL (${C.length}):
|
|
85
|
+
`);for(let[h,T]of C){let E=T.category||"",D=T.summary||"";console.log(` ${h}${E?` [${E}]`:""} \u2192 schedule_call`),D&&console.log(` ${D}`)}console.log()}if(R.length>0){console.log(` REPLY NEEDED (${R.length}):
|
|
86
|
+
`);let h=20;for(let[T,E]of R.slice(0,h)){let D=E.category||"",M=E.suggestedPriority?`[${E.suggestedPriority}]`:"",Ie=(E.autoTags||[]).slice(0,4).join(", "),we=E.summary||"",xe=[...E.messages||[]].reverse().find(be=>be.sender!==P&&be.sender!=="Unknown")?.text?.slice(0,80)||E.lastMessagePreview?.slice(0,80)||"";console.log(` ${M} ${T}${D?` [${D}]`:""}`),console.log(we?` ${we}`:` "${xe}"`),Ie&&console.log(` #${Ie.replace(/, /g," #")}`)}R.length>h&&console.log(`
|
|
87
|
+
... and ${R.length-h} more`)}p.length===0&&console.log(" All caught up \u2014 no unanswered messages.");let I=new Set(Object.keys(e)),He=new Set(Object.values(e).map(h=>h.profileUrl).filter(Boolean)),Se=Object.entries(r).filter(([h,T])=>T.connectionStatus==="connected"&&T.personActionNeeded==="initiate_contact"&&!He.has(h)&&!(T.name&&I.has(T.name))).sort((h,T)=>{let E={high:0,medium:1,low:2}[h[1].personPriority]??3,D={high:0,medium:1,low:2}[T[1].personPriority]??3;return E!==D?E-D:(T[1].connectionDate||"").localeCompare(h[1].connectionDate||"")});if(Se.length>0){console.log(`
|
|
88
|
+
INITIATE CONTACT (${Se.length}):
|
|
89
|
+
`);let h=20;for(let[T,E]of Se.slice(0,h)){let D=E.personPriority?`[${E.personPriority.toUpperCase()}]`:"",M=E.personSummary||E.title||"";console.log(` ${D} ${E.name||"Unknown"} \u2014 ${M}`)}Se.length>h&&console.log(`
|
|
90
|
+
... and ${Se.length-h} more`)}let At=s.filter(([,h])=>h.conversationStatus==="awaiting_reply");At.length>0&&console.log(`
|
|
91
|
+
BALL IN THEIR COURT: ${At.length} conversations`),L&&console.log(`
|
|
92
92
|
[DRY RUN \u2014 no changes written]`),console.log(`
|
|
93
93
|
\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
|
|
94
|
-
`)}async function
|
|
94
|
+
`)}async function qs(e,t){console.log(`
|
|
95
95
|
\u2550\u2550 BACKFILL: Reading conversations with 0 messages \u2550\u2550
|
|
96
|
-
`);let o=Object.entries(t).filter(([,
|
|
97
|
-
`);let s=[],r=0,i=0,a=[];for(let
|
|
98
|
-
Backfill complete: ${s.length} read, ${r} failed${i?`, ${i} skipped on CDP recovery`:""}`),o.length>n&&console.log(` ${o.length-n} more remaining (run again or increase --max)`),s}function
|
|
96
|
+
`);let o=Object.entries(t).filter(([,c])=>!c.messages||c.messages.length===0).map(([c,l])=>({name:c,preview:l.lastMessagePreview||"",lastBy:l.lastMessageBy}));if(o.length===0)return console.log(" All conversations already have messages."),[];o.sort((c,l)=>c.lastBy==="them"&&l.lastBy!=="them"?-1:c.lastBy!=="them"&&l.lastBy==="them"?1:0);let n=Math.min(o.length,Oe);console.log(` ${o.length} conversations without messages, reading ${n} (--max=${Oe})
|
|
97
|
+
`);let s=[],r=0,i=0,a=[];for(let c=0;c<n;c++){let{name:l}=o[c];console.log(` [${c+1}/${n}] ${l}`);let f=e.page;try{if(await Rt(f),!(await ct(f,l)).verified){console.log(" Not found or thread didn't load."),r++,await Z(f),await _(500);continue}let u=await rt(f);if(!u||u.length===0){console.log(" No messages extracted."),r++,await Z(f),await _(500);continue}if(!it(u,l,a)){console.log(" Skipped \u2014 stale data."),r++,await Z(f),await _(500);continue}let S=await st(f),y=t[l]||{profileUrl:null,lastSynced:new Date().toISOString(),lastMessageBy:null,lastMessagePreview:"",lastMessageTime:"",unread:!1,conversationStatus:null,messages:[]};y.messages=at(y.messages,u),y.lastSynced=new Date().toISOString(),S&&(y.profileUrl=S),t[l]=y,L||X(t),console.log(` \u2713 ${y.messages.length} msgs${S?" +url":""}`),s.push({name:l}),a=[...new Set(u.map(v=>v.sender).filter(v=>v&&v!=="Unknown"))],await Z(f),await _(500)}catch(d){if(!le(d))throw d;i++,await Je(e,`Backfill thread ${c+1}/${n} (${l})`)}}return console.log(`
|
|
98
|
+
Backfill complete: ${s.length} read, ${r} failed${i?`, ${i} skipped on CDP recovery`:""}`),o.length>n&&console.log(` ${o.length-n} more remaining (run again or increase --max)`),s}function Js(e){let t=qt(),o=Object.values(e),n=o.filter(c=>c.sources?.includes("recruiters-from-jobs")&&!c.connectionStatus).length,s=o.filter(c=>c.connectionStatus==="modal_failed").length,r=n+s,i=o.filter(c=>c.connectionStatus==="connected"&&c.connectionMethod).length,a=o.filter(c=>c.connectionStatus==="pending"||c.connectionStatus==="sent_without_note").length;console.log("\u2550\u2550\u2550 CONNECTION QUOTA & QUEUE \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550"),console.log(` Today: ${t.daily.sent}/${t.daily.max} sent (${t.daily.remaining} remaining)`),console.log(` Week: ${t.weekly.sent}/${t.weekly.max} sent (${t.weekly.remaining} remaining)`),console.log(` Queue: ${r} profiles (${n} new, ${s} retry)`),console.log(` Funnel: ${i} accepted, ${a} pending`),r===0&&console.log(" \u26A0 Queue empty \u2014 need new job applications to feed funnel"),console.log("\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550")}function Hs(e){let t=Object.values(e).filter(o=>o.jobCount>=2).sort((o,n)=>(n.jobCount||0)-(o.jobCount||0)).slice(0,10);if(t.length!==0){console.log("\u2550\u2550\u2550 TOP RECRUITERS (by job count) \u2550\u2550\u2550");for(let o=0;o<t.length;o++){let n=t[o],s=n.connectionStatus==="connected"?"connected":n.connectionStatus==="pending"||n.connectionStatus==="sent_without_note"?"pending":n.connectionStatus||"unknown",r=n.lastJobTitle?G(n.lastJobTitle,25):"",i=n.lastJobDate||"",a=r?` (last: ${r}${i?", "+i:""})`:"";console.log(` ${String(o+1).padStart(2)}. ${G(n.name||"?",25).padEnd(25)} ${String(n.jobCount).padStart(2)} jobs | ${s.padEnd(10)}${a}`)}console.log("\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550")}}function Vs(){let e=zt(10);if(e.length!==0){console.log("\u2550\u2550\u2550 TOP JOBS TO APPLY \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550");for(let t=0;t<e.length;t++){let{score:o,job:n}=e[t],s=G(n.title||"?",30),r=G(n.company||"?",15),i=n.salary?` \u2014 ${G(n.salary,12)}`:"",a=n.location?` \u2014 ${G(n.location,15)}`:"",c=n.recruiterUrl?" \u{1F464}":"";console.log(` ${String(t+1).padStart(2)}. [${String(o).padStart(2)}] ${s} @ ${r}${i}${a}${c}`)}console.log(" Apply: npm run apply-queue"),console.log("\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550")}}function Ws(){let e=Yt();e.totalJobs===0&&e.totalVisited===0||(console.log("\u2550\u2550\u2550 MARKET RESEARCH \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550"),console.log(` Scouted jobs: ${e.totalJobs}${e.lastScoutDate?` (last: ${e.lastScoutDate})`:""}`),console.log(` Discovered people: ${e.totalRecruiters}`),console.log(` Profile visits: ${e.totalVisited} unique (${e.todayVisited} today)`),e.topTech.length>0&&console.log(` Top tech: ${e.topTech.slice(0,5).join(", ")}`),e.topCompanies.length>0&&console.log(` Top companies: ${e.topCompanies.slice(0,5).join(", ")}`),console.log("\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550"))}function Ys(){let e=Ht();if(e.totalApplications!==0){if(console.log(`\u2550\u2550\u2550 FAQ STATS (${e.totalApplications} applications) \u2550\u2550`),console.log(` Unique questions: ${e.uniqueQuestions}`),e.topQuestions.length>0){console.log(" Top questions:");for(let t of e.topQuestions.slice(0,8)){let o=G(t.topAnswer,30);console.log(` "${G(t.question,40)}" \u2014 ${t.count}x \u2192 ${o}`)}}if(e.inconsistent.length>0){console.log(` Inconsistent answers: ${e.inconsistent.length} \u26A0`);for(let t of e.inconsistent.slice(0,3))console.log(` "${G(t.question,35)}" \u2014 ${t.answers.join(", ")}`)}console.log("\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550")}}function G(e,t){return e?e.length>t?e.slice(0,t-1)+"\u2026":e:""}function Gs(){let e=Te(),t=W(),o=new Set(Object.keys(e)),n=new Set(Object.values(e).map(g=>g.profileUrl).filter(Boolean));console.log(`
|
|
99
99
|
\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550`),console.log(" TODAY'S PRIORITIES"),console.log(`\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
|
|
100
|
-
`),console.log(
|
|
101
|
-
`);for(let p=0;p<g.length;p++){let[w,
|
|
102
|
-
`);for(let[g,p]of
|
|
103
|
-
`);for(let p=0;p<g.length;p++){let[w,
|
|
104
|
-
`);for(let p=0;p<g.length;p++){let[w,
|
|
105
|
-
`);for(let p=0;p<g.length;p++){let[w,
|
|
106
|
-
`);for(let p=0;p<g.length;p++){let[w,
|
|
107
|
-
`);for(let p=0;p<g.length;p++){let[,w]=g[p],
|
|
100
|
+
`),console.log(po()),console.log(""),Js(t),console.log(""),Hs(t),console.log(""),Ws(),console.log(""),Vs(),console.log(""),Ys(),console.log("");let s=Object.entries(e),r=s.filter(([,g])=>g.unread===!0).sort((g,p)=>(p[1].lastMessageTime||"").localeCompare(g[1].lastMessageTime||""));if(r.length>0){let g=r.slice(0,10);console.log(` UNREAD ON LINKEDIN (${r.length}) \u2014 bold in LinkedIn UI, not yet opened:
|
|
101
|
+
`);for(let p=0;p<g.length;p++){let[w,C]=g[p],R=C.category?` [${C.category}]`:"",I=C.summary||(C.personContext?.title?`${C.personContext.title}${C.personContext.company?` at ${C.personContext.company}`:""}`:null)||C.lastMessagePreview?.slice(0,60)||null;console.log(` ${p+1}. ${w}${R}${I?` \u2014 ${I}`:""}`)}r.length>10&&console.log(` ... +${r.length-10} more`),console.log()}let i=new Date().toISOString().slice(0,10),a=s.filter(([,g])=>g.followUpNote),c=a.filter(([,g])=>!g.followUpDate||g.followUpDate<=i),l=a.filter(([,g])=>g.followUpDate&&g.followUpDate>i);if(a.length>0){console.log(` FOLLOW-UPS (${c.length} due, ${l.length} scheduled):
|
|
102
|
+
`);for(let[g,p]of c){let w=p.followUpDate?` [due ${p.followUpDate}]`:" [no date]";console.log(` \u26A1 ${g}${w} \u2014 ${p.followUpNote}`)}for(let[g,p]of l.slice(0,5))console.log(` \u{1F4C5} ${g} [${p.followUpDate}] \u2014 ${p.followUpNote}`);l.length>5&&console.log(` ... +${l.length-5} more scheduled`),console.log()}let f=g=>g.actionNeeded&&g.actionNeeded!=="none"&&(!g.reviewedAtPreview||De(g.lastMessagePreview)!==De(g.reviewedAtPreview)),d=s.filter(([,g])=>f(g)).sort((g,p)=>(p[1].lastSynced||"").localeCompare(g[1].lastSynced||""));if(d.length>0){let g=d.slice(0,20);console.log(` UNREVIEWED \u2014 NEW MESSAGES (${d.length}):
|
|
103
|
+
`);for(let p=0;p<g.length;p++){let[w,C]=g[p],R=C.priority||C.suggestedPriority||"",I=C.category?` [${C.category}]`:"",He=C.summary||"";console.log(` ${p+1}. ${R?`[${R}] `:""}${w}${I} \u2014 ${He}`)}d.length>20&&console.log(` ... +${d.length-20} more`),console.log()}let u=s.filter(([,g])=>g.actionNeeded==="schedule_call").sort((g,p)=>{let w={high:0,medium:1,low:2}[g[1].suggestedPriority]??3,C={high:0,medium:1,low:2}[p[1].suggestedPriority]??3;return w-C});if(u.length>0){let g=u.slice(0,5);console.log(` CALLS TO SCHEDULE (${u.length}):
|
|
104
|
+
`);for(let p=0;p<g.length;p++){let[w,C]=g[p],R=C.summary||C.personContext?.title||"";console.log(` ${p+1}. ${w} \u2014 ${R}`)}u.length>5&&console.log(` ... +${u.length-5} more`),console.log()}let m=s.filter(([,g])=>g.conversationStatus==="unanswered_by_us"&&g.actionNeeded!=="schedule_call"&&(g.suggestedPriority==="high"||g.priority==="high")&&!f(g)).sort((g,p)=>(p[1].lastSynced||"").localeCompare(g[1].lastSynced||""));if(m.length>0){let g=m.slice(0,8);console.log(` REPLY NEEDED \u2014 HIGH (${m.length}):
|
|
105
|
+
`);for(let p=0;p<g.length;p++){let[w,C]=g[p],R=C.category?` [${C.category}]`:"",I=C.summary||"";console.log(` ${p+1}. ${w}${R} \u2014 ${I}`)}m.length>8&&console.log(` ... +${m.length-8} more`),console.log()}let S=s.filter(([,g])=>g.conversationStatus==="unanswered_by_us"&&g.actionNeeded!=="schedule_call"&&g.suggestedPriority==="medium"&&g.priority!=="high"&&!f(g)).sort((g,p)=>(p[1].lastSynced||"").localeCompare(g[1].lastSynced||""));if(S.length>0){let g=S.slice(0,5);console.log(` REPLY NEEDED \u2014 MEDIUM (${S.length}):
|
|
106
|
+
`);for(let p=0;p<g.length;p++){let[w,C]=g[p],R=C.category?` [${C.category}]`:"",I=C.summary||"";console.log(` ${p+1}. ${w}${R} \u2014 ${I}`)}S.length>5&&console.log(` ... +${S.length-5} more`),console.log()}let y=Object.entries(t).filter(([g,p])=>p.connectionStatus==="connected"&&p.personActionNeeded==="initiate_contact"&&!n.has(g)&&!(p.name&&o.has(p.name))).sort((g,p)=>{let w={high:0,medium:1,low:2}[g[1].personPriority]??3,C={high:0,medium:1,low:2}[p[1].personPriority]??3;return w!==C?w-C:(p[1].connectionDate||"").localeCompare(g[1].connectionDate||"")});if(y.length>0){let g=y.slice(0,10);console.log(` NEW CONNECTIONS TO MESSAGE (${y.length}):
|
|
107
|
+
`);for(let p=0;p<g.length;p++){let[,w]=g[p],C=w.personPriority?`[${w.personPriority.toUpperCase()}]`:"",R=w.connectionDate?zs(w.connectionDate):"",I=w.title?`${w.title}${w.company?` at ${w.company}`:""}`:w.company||"";console.log(` ${p+1}. ${C} ${w.name||"Unknown"} \u2014 ${I}${R?` (${R})`:""}`)}y.length>10&&console.log(` ... +${y.length-10} more`),console.log()}let v=s.filter(([,g])=>ye(g));if(v.length>0){let g=v.filter(([,w])=>ye(w)===j.CONFIRMED).length,p=v.filter(([,w])=>{let C=ye(w);return C===j.DRAFT||C===j.NEEDS_REVIEW}).length;console.log(` DRAFTS: ${v.length} (${g} confirmed, ${p} pending)`),g>0&&console.log(" Send: npm run messages -- --push-drafts"),p>0&&console.log(" Review: npm run messages -- --review-drafts"),console.log()}let $=s.filter(([,g])=>g.conversationStatus==="unanswered_by_us").length,k=s.filter(([,g])=>g.conversationStatus==="awaiting_reply").length;console.log(` Summary: ${r.length} unread on LinkedIn, ${d.length} unreviewed, ${$} need reply, ${y.length} new connections, ${k} ball in their court`),console.log(`
|
|
108
108
|
\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
|
|
109
|
-
`)}function
|
|
109
|
+
`)}function zs(e){let t=new Date(e);if(isNaN(t))return"";let o=Math.floor((Date.now()-t.getTime())/864e5);return o===0?"today":o===1?"1d ago":`${o}d ago`}function Ks(){let e=Te();_t(e,null,null)}async function Qs(){if(console.log(`
|
|
110
110
|
LinkedIn Sync-All
|
|
111
|
-
`),
|
|
112
|
-
\u26A0 Phase 1 CDP dead (attempt ${
|
|
113
|
-
CDP page recoveries this run: ${
|
|
114
|
-
Error:`,i.message),
|
|
111
|
+
`),Ao){Gs();return}if(To){let i=Ue("sync-all","report");Ks(),he(i,"success");return}if(_o){let i=Ue("sync-all","enrich");try{Bs(),he(i,"success")}catch(a){throw he(i,"error",null,a.message),a}return}let e=Eo?"backfill":Ct?"full":"incremental",t=Ue("sync-all",e),o={scanned:0,toReRead:0,threadsRead:0,newMessages:0};L&&console.log(" [DRY RUN mode]"),console.log(` Mode: ${e} | Max threads to read: ${Oe}`);let n=Te(),s=await eo(),r={browser:s.browser,page:s.page};try{await ot(r.page);let i=null,a=[];if(Eo)a=await qs(r,n);else{let c=0;for(;;)try{i=await Fs(r.page,n);break}catch(u){if(!le(u))throw u;if(c++,c>=kt)throw new Error(`Phase 1 failed ${kt} times on CDP death \u2014 giving up. Last: ${u.message}`);console.log(`
|
|
112
|
+
\u26A0 Phase 1 CDP dead (attempt ${c}/${kt}). Recovering and restarting Phase 1...`),await Je(r,`Phase 1 attempt ${c}`)}let l=new Set;a=await Ls(r,i.toReRead,n,l),o.scanned=i.totalCards||0,o.toReRead=i.toReRead?.size||0;let f=new Set(a.map(u=>u.name)),d=0;for(let[u,{card:m}]of i.toReRead){if(f.has(u)||l.has(u))continue;let S=n[u];S&&(S.lastMessageBy=m.lastMessageBy,S.lastMessageTime=m.lastMessageTime||S.lastMessageTime,S.unread=m.unread,d++)}d>0&&(console.log(` Pending re-read: ${d} threads (raise --max or re-run to drain).`),L||X(n))}o.threadsRead=a.length,o.newMessages=a.reduce((c,l)=>c+(l.newMessages||0),0),a.length>0&&Us(n,a),_t(n,i,a,{unreadCards:i?.unreadCards,readUnread:Et}),ue.pageRecoveries>0&&console.log(`
|
|
113
|
+
CDP page recoveries this run: ${ue.pageRecoveries}`),L||et(),he(t,"success",o)}catch(i){console.error(`
|
|
114
|
+
Error:`,i.message),Tt&&console.error(i.stack),he(t,"error",o,i.message)}finally{if(!L&&!To&&!_o&&!Ao)try{et()}catch{}await r.browser.close()}}Qs();
|