ilml-plugin-linkedin 1.11.3 → 1.11.5
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 +8 -0
- package/dist/syncAll.mjs +51 -51
- package/ilml-plugin.json +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.11.5
|
|
4
|
+
- **`lastMessageBy` now reflects who actually wrote the latest message.** For InMail / starred / subject-line threads, LinkedIn's sidebar snippet never shows the "You:" prefix, so the card extractor defaulted `lastMessageBy` to `'them'` even when the latest message was yours — `today` and other reports would then list those threads under "ball in their court" or "need reply" indefinitely. After a Phase 2 re-read, sync now derives `lastMessageBy` from the tail of `messages[]` (the authoritative source) instead of trusting the card. The card-based `sender_changed` re-read trigger is also tightened: it now fires only on the positive "card says we wrote" signal, not on the unreliable `'them'` fallback.
|
|
5
|
+
- No schema migration; existing data files unchanged.
|
|
6
|
+
|
|
7
|
+
## 1.11.4
|
|
8
|
+
- **`sync-all` now catches new activity that LinkedIn's sidebar hides in preview text.** When LinkedIn shows a thread's subject line (or any other static text) as the sidebar preview — common for InMail / starred / sponsored conversations — the preview never changes when new messages arrive, so the previous "preview_changed" check missed those threads forever. The sync now also re-reads a thread when the sidebar's `lastMessageTime` is newer than our own `lastSynced` for that thread, which catches activity regardless of what the card text shows. Self-correcting: once Phase 2 re-reads and updates `lastSynced`, the check stays quiet until the next real activity.
|
|
9
|
+
- No schema migration; existing data files unchanged.
|
|
10
|
+
|
|
3
11
|
## 1.11.3
|
|
4
12
|
- **Fixes plugin ignoring your configured `DATA_DIR` in CLI mode.** `ilml linkedin today` and other read-only commands were silently reading from an empty folder inside the plugin install dir instead of the `DATA_DIR` you set via `ilml plugin config`. Reports showed zeros even on databases with thousands of conversations. No data loss — just the wrong folder was being read.
|
|
5
13
|
|
package/dist/syncAll.mjs
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
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 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 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 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=ve(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=ve(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=ve(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=ve(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=ve(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 M=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 Oo}from"url";import{execSync as Ye}from"child_process";var Io=Oo(import.meta.url),xo=x.dirname(x.dirname(Io)),U=process.env.DATA_DIR?x.resolve(process.env.DATA_DIR,"collected-profiles"):x.join(xo,"collected-profiles"),Ge=x.join(U,"people.json"),Qs=x.join(U,"jobs.json"),jo=x.join(U,"quota.json"),je=x.join(U,"conversations.json"),jt=x.join(U,"sync-state.json"),Rt=x.join(U,"application-questions.json"),ze=x.join(U,"profile-history"),Dt=20,Ot=100,de=null,Mo=null,Q=null,ne=null,pe=null,oe=null;function Mt(){de=null,Mo=null,Q=null,ne=null,pe=null,oe=null}function K(){O.existsSync(U)||O.mkdirSync(U,{recursive:!0})}function Ce(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 ke(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 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?x.join(ze,`${t}.json`):null}function Lo(e){let t=Pt(e);return t?ke(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{Ce(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 W(){return de||(K(),de=ke(Ge,"people.json")||{},de)}function Me(e){K(),de=e;try{Ce(Ge,e)}catch(t){console.error(`[!] Failed to save people.json: ${t.message}`),console.error(` Path: ${x.resolve(Ge)}`)}}function Te(){return ne||(K(),ne=ke(je,"conversations.json")||{},Jo(ne),ne)}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{Ce(je,e)}catch{}}}function X(e){K(),ne=e;try{Ce(je,e)}catch(t){console.error(`[!] Failed to save conversations.json: ${t.message}`),console.error(` Path: ${x.resolve(je)}`)}}function Ft(e,t={}){if(!e)return null;let o=W(),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 Me(o),o[e]}function Ho(){return Q||(K(),Q=ke(jo,"quota.json"),(!Q||!Q.connections)&&(Q={connections:[]}),Q)}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 pe||(K(),pe=ke(jt,"sync-state.json")||{},pe)}function Ut(e){K(),pe=e;try{Ce(jt,e)}catch(t){console.error(`[!] Failed to save sync-state.json: ${t.message}`)}}function Wo(){if(oe)return oe;K();try{O.existsSync(Rt)&&(oe=JSON.parse(O.readFileSync(Rt,"utf-8")))}catch{}return oe||(oe={}),oe}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(x.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 re 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)),Re=process.env.DATA_DIR?F.resolve(process.env.DATA_DIR,"market-research"):F.join(Ko,"market-research"),Xo=F.join(Re,"scouted-jobs.json"),Zo=F.join(Re,"discovered-people.json"),en=F.join(Re,"visit-log.json"),er=F.join(Re,"companies.json"),tn=F.join(Re,"job-decisions.json");function Ne(e){try{if(re.existsSync(e))return JSON.parse(re.readFileSync(e,"utf-8"))}catch{}return null}function qt(){Ae=null,Ee=null,_e=null,nn=null,se=null}var Ae=null;function Ze(){return Ae||(Ae=Ne(Xo)||{},Ae)}var Ee=null;function Jt(){return Ee||(Ee=Ne(tn)||{},Ee)}var _e=null;function on(){return _e||(_e=Ne(Zo)||{},_e)}var nn=null;var se=null;function sn(){return se||(se=Ne(en)||{visits:[]},Array.isArray(se.visits)||(se.visits=[]),se)}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=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},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 Pe(){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=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 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 Fe(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=M.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 ge(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=>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))),l=e.filter(u=>fe.has(u.status)),c=[...r,...a,...i],f=[];for(let u of c){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 C=ge($.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),Fe(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 Fe(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 Fe(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 Fe(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 Z(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=>!fe.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===M?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!==M&&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!==M&&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===M)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)}: ${me(i,40)}`):i?s.push(me(i,50)):s.push(t.split(" ")[0]);let c=n.filter(S=>S.sender!==M&&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=>!fe.has(c.status)),r=s.map(c=>c.text).join(" "),i=s.filter(c=>c.sender!==M&&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=>$e.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&&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:`${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===M?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 De(e){return(e||"").replace(/\s+/g," ").trim().slice(0,80).toLowerCase()}function ct(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 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(`${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 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 ee 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(`
|
|
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
8
|
`),process.exit(1));var io=Ln({token:ro});function qn(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 Jn({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?` ${qn(s)}`:"",l=o==="error"?`error${n?`: ${String(n).slice(0,80)}`:""}`:o==="quota-reached"?"daily quota reached":"done",c=t&&t!=="default"?` (${t})`:"";return`${r} ${e}${c} \u2014 ${l} \u2014 ${i}${a}`}async function ao({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 l=`**${Jn({scriptName:e,mode:t,status:o,error:s,duration:r})}**
|
|
9
9
|
|
|
10
10
|
${n.join(`
|
|
11
|
-
`)}`;try{await io.addMessage(i,l)}catch(c){console.error(`[runReport] Failed to post end-of-session message: ${c.message}`)}}var Vn=Hn(import.meta.url),Wn=
|
|
12
|
-
`)}import"dotenv/config";import*as b from"fs";import*as yt from"os";import*as _ from"path";import{fileURLToPath as is}from"url";var gt={};Tt(gt,{NAME:()=>Kn,VERSION:()=>Qn,up:()=>es});import*as q from"fs";import*as
|
|
11
|
+
`)}`;try{await io.addMessage(i,l)}catch(c){console.error(`[runReport] Failed to post end-of-session message: ${c.message}`)}}var Vn=Hn(import.meta.url),Wn=ee.dirname(ee.dirname(Vn)),ut=process.env.DATA_DIR?ee.resolve(process.env.DATA_DIR,"collected-profiles"):ee.join(Wn,"collected-profiles"),ft=ee.join(ut,"run-log.json");function Yn(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(ft))return{runs:[]};let e=JSON.parse(B.readFileSync(ft,"utf-8"));return!e.runs||!Array.isArray(e.runs)?{runs:[]}:e}catch{return{runs:[]}}}function lo(e){B.existsSync(ut)||B.mkdirSync(ut,{recursive:!0});try{Yn(ft,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),lo(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 l=i.runs.find(c=>c.script===e.script&&c.status==="running"&&c.timestamp===new Date(e.startTime).toISOString());l&&(l.status=t,l.result=t,l.duration=Date.now()-e.startTime,l.stats=o,l.error=n,s=l.duration)}lo(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";ao({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 Gn(){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 zn(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 co(){let e=["sync-all","messages","funnel","sync","apply"],t=Le(),o=Gn(),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"),l=i[i.length-1],c=a||l,f=c?zn(c.timestamp):"never",u=(c?Math.floor((Date.now()-new Date(c.timestamp).getTime())/864e5):1/0)>1?" \u26A0":c?.status==="error"?" \u2717":" \u2713",m="";if(c?.stats){let y=c.stats,v=Object.entries(y).filter(([,$])=>typeof $=="number"&&$>0).slice(0,3).map(([$,C])=>`${C} ${$}`);v.length&&(m=` (${v.join(", ")})`)}let S=c?.duration?` ${Math.round(c.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 yt from"os";import*as _ from"path";import{fileURLToPath as is}from"url";var gt={};Tt(gt,{NAME:()=>Kn,VERSION:()=>Qn,up:()=>es});import*as q from"fs";import*as Be from"path";var Qn=1,Kn="normalize-jobs-and-questions",Xn=["description","techStack","jobTags","seniority","employmentType","industries","jobFunction","postedTime","applicantCount","companySize","workplaceType","contractType","isEasyApply","applyType","companyUrl","scoutedAt","lastSeen"],Zn=50;function dt(e){return q.existsSync(e)?JSON.parse(q.readFileSync(e,"utf-8")):null}function pt(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 es(e,t){let o=Be.join(e,"jobs.json"),n=Be.join(t,"scouted-jobs.json"),s=Be.join(e,"application-questions.json"),r=dt(o)||{},i=dt(n)||{},a=0,l=[];for(let f of Object.keys(r)){if(!i[f])continue;let d=i[f],u=r[f];for(let m of Xn)d[m]!=null&&u[m]==null&&(u[m]=d[m]);l.push(f),a++}a>0?(pt(o,r),console.log(` Phase 1: Merged ${a} scouted jobs into jobs.json`)):console.log(" Phase 1: No overlapping jobs to merge");let c=dt(s);if(c&&Array.isArray(c.applications)){let f={},d=0;for(let u of c.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<=Zn?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})}pt(s,f),console.log(` Phase 2: Rekeyed ${c.applications.length} applications by jobId (${Object.keys(f).length} unique jobs)`),d>0&&console.log(` Phase 2: Stripped bloated options from ${d} questions`)}else c&&!c.applications?console.log(" Phase 2: application-questions.json already in keyed format"):console.log(" Phase 2: No application-questions.json found");if(l.length>0){for(let f of l)delete i[f];pt(n,i),console.log(` Phase 3: Removed ${l.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 mt={};Tt(mt,{NAME:()=>os,VERSION:()=>ts,up:()=>rs});import*as J from"fs";import*as uo from"path";var ts=2,os="reset-unread-flags";function ns(e){return J.existsSync(e)?JSON.parse(J.readFileSync(e,"utf-8")):null}function ss(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 rs(e){let t=uo.join(e,"conversations.json"),o=ns(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}ss(t,o),console.log(` Reset unread=false on ${n} conversations. Run 'ilml linkedin sync-all' to re-establish real unread state from LinkedIn.`)}var as=is(import.meta.url),ls=_.dirname(as),cs=_.dirname(_.dirname(ls));function us(e){return e&&(e==="~"?yt.homedir():e.startsWith("~/")||e.startsWith("~\\")?_.join(yt.homedir(),e.slice(2)):e)}var H=process.env.DATA_DIR?_.resolve(us(process.env.DATA_DIR)):cs,ae=_.join(H,"collected-profiles"),le=_.join(H,"market-research"),N=_.join(H,".schema-version"),fo=[_.join(ae,".schema-version"),_.join(le,".schema-version")],te=2,Y=[{version:1,module:gt},{version:2,module:mt}];(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 ho(){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
14
|
`),process.exit(1));try{b.mkdirSync(H,{recursive:!0})}catch(t){t.code!=="EEXIST"&&(console.error(`
|
|
15
15
|
[FATAL] Cannot create DATA_DIR (${H}): ${t.message}`),console.error(` Fix the path or permissions and try again.
|
|
@@ -17,57 +17,57 @@ ${n.join(`
|
|
|
17
17
|
[FATAL] DATA_DIR (${H}) is not accessible: ${t.message}
|
|
18
18
|
`),process.exit(1)}e.isDirectory()||(console.error(`
|
|
19
19
|
[FATAL] DATA_DIR (${H}) 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[
|
|
20
|
+
`),process.exit(1));for(let t of[ae,le]){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=_.join(H,".migration-lock");function fs(){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 St(){try{if(JSON.parse(b.readFileSync(ie,"utf-8")).pid!==process.pid)return;b.unlinkSync(ie)}catch{}}var po=!1;function ds(){if(po)return;po=!0;let e=()=>{try{St()}catch{}process.exit(1)};for(let t of["SIGINT","SIGTERM","SIGHUP"])try{process.on(t,e)}catch{}}function ps(){if(b.existsSync(N))return;let e=[],t=[];for(let r of fo)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] ${
|
|
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 fo)if(b.existsSync(r))try{b.unlinkSync(r)}catch{}console.log(` Consolidated legacy .schema-version files \u2192 ${N}`)}function gs(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 go(){if(ps(),!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
32
|
`),process.exit(1)}let t=gs(e);return t===null&&(console.error(`
|
|
33
|
-
[FATAL] ${
|
|
34
|
-
`),process.exit(1)),t}function yo(e){try{b.chmodSync(e,384)}catch{}}function ms(e){if(ho(),b.existsSync(
|
|
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${
|
|
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 yo(e){try{b.chmodSync(e,384)}catch{}}function ms(e){if(ho(),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)),yo(t),b.renameSync(t,N)}catch(o){try{b.unlinkSync(t)}catch{}throw o}}function mo(e,t){if(!b.existsSync(e))return;let o=`.pre-v${String(t).padStart(3,"0")}.backup`,n=b.readdirSync(e).filter(a=>{let l=a.toLowerCase();if(l.includes(".backup")||l===".schema-version"||l.endsWith(".tmp"))return!1;try{return b.statSync(_.join(e,a)).isFile()}catch{return!1}}),s=0,r=0,i=[];for(let a of n){let l=_.join(e,a),c=_.join(e,a+o);if(b.existsSync(c)){r++;continue}try{wo(l,c),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 ${_.basename(e)}/: `+i.map(l=>`${l.file} (${l.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 ${_.basename(e)}/ (${o})${a}`)}}var ht=!1;async function So(){if(ht)return;ho(),bs();let t=go().version;if(t===te){ht=!0;return}ds(),fs(),process.on("exit",St),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=vs();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{mo(
|
|
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{mo(ae,s.version),mo(le,s.version)}catch(l){console.error(`
|
|
43
43
|
[FATAL] Pre-migration backup failed for ${r}: ${l.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(
|
|
44
|
+
`),process.exit(1)}let a=s.module;try{await a.up(ae,le)}catch(l){console.error(`
|
|
45
45
|
[FATAL] Migration ${r} failed: ${l.message}`),console.error(` Attempting auto-rollback from ${i} files...`);try{let c=$s(i);c.ok?(console.error(` \u2713 Auto-rollback restored ${c.restored} files. Schema stays at v${t}.`),console.error(` The migration will be retried on next plugin command. Fix the root cause first.
|
|
46
46
|
`)):(console.error(` [!] Auto-rollback INCOMPLETE: ${c.error}`),console.error(` Manual recovery: copy *${i} files back over the originals.
|
|
47
47
|
`))}catch(c){console.error(` [!] Auto-rollback CRASHED while restoring: ${c.message}`),console.error(` Manual recovery: copy *${i} files back over the originals.
|
|
48
48
|
`)}process.exit(1)}try{let l=new Date().toISOString(),c={version:s.version,name:a.NAME||r,appliedAt:l},f=go();f.version=s.version,f.migratedAt=l,f.migrations=f.migrations||[],f.migrations.push(c),ms(f)}catch(l){console.error(`
|
|
49
49
|
[FATAL] Migration ${r} applied successfully but schema version file could NOT be updated: ${l.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
50
|
`),process.exit(1)}console.log(` \u2713 Migration ${r} complete`)}try{Mt()}catch{}try{qt()}catch{}ht=!0,St(),console.log(`
|
|
51
|
-
\u2713 Schema is now at v${
|
|
52
|
-
`)}var hs=[/\.json\.tmp$/,/\.schema-version\.tmp$/,/\.backup\.tmp$/];function ys(e){let t=e.toLowerCase();return hs.some(o=>o.test(t))}var Ss=6e4;function ws(){let e=Date.now(),t=[];for(let o of[H,
|
|
53
|
-
\u{1F504} CDP dead \u2014 recovering page (#${
|
|
54
|
-
`)}function
|
|
51
|
+
\u2713 Schema is now at v${te}
|
|
52
|
+
`)}var hs=[/\.json\.tmp$/,/\.schema-version\.tmp$/,/\.backup\.tmp$/];function ys(e){let t=e.toLowerCase();return hs.some(o=>o.test(t))}var Ss=6e4;function ws(){let e=Date.now(),t=[];for(let o of[H,ae,le]){if(!b.existsSync(o))continue;let n;try{n=b.readdirSync(o)}catch{continue}for(let s of n){if(!ys(s))continue;let r=_.join(o,s);try{let i=b.statSync(r);if(!i.isFile()||e-i.mtimeMs<Ss)continue;t.push({dir:o,file:s,fullPath:r})}catch{}}}return t}function bs({verbose:e=!1}={}){let t=ws();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: ${_.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 vs(){let e=[];for(let t of[ae,le]){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=_.join(t,n);try{qe(s)}catch(r){e.push({dir:_.basename(t),file:n,err:r.message})}}}return e.length===0?{ok:!0}:{ok:!1,broken:e}}function $s(e){let t=[],o=[];for(let r of[ae,le]){if(!b.existsSync(r))continue;let i=b.readdirSync(r).filter(a=>a.endsWith(e));for(let a of i){let l=_.join(r,a),c=a.slice(0,-e.length),f=_.join(r,c);try{c.toLowerCase().endsWith(".json")?qe(l):b.accessSync(l,b.constants.R_OK),t.push({backupPath:l,originalPath:f,fileName:a,dir:r})}catch(d){o.push(`${_.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:l}of t)try{wo(r,i),n++}catch(c){s.push(`${_.basename(l)}/${a}: ${c.message}`)}return s.length>0?{ok:!1,error:s.join("; "),restored:n}:{ok:!0,restored:n}}function wo(e,t){let o=t+".tmp";try{b.copyFileSync(e,o),yo(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 Cs(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 ce(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 bo(e,t,{returnUrl:o,gotoTimeoutMs:n=6e4,cookiesProvider:s=null,setupPage:r=null}={}){try{await Cs(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 So();var To="https://www.linkedin.com/messaging/",ks=10,wt=3,Ts=async e=>{await e.evaluateOnNewDocument(()=>{Object.defineProperty(navigator,"webdriver",{get:()=>{}})})},As=()=>Pe(),ue={pageRecoveries:0};async function Je(e,t){if(ue.pageRecoveries>=ks)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 bo(e.browser,e.page,{returnUrl:To,cookiesProvider:As,setupPage:Ts}),ue.pageRecoveries++,await E(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),bt=z.includes("--full"),L=z.includes("--dry-run"),vo=z.includes("--report"),vt=z.includes("--verbose"),$o=z.includes("--backfill"),Co=z.includes("--enrich"),ko=z.includes("--today"),$t=z.includes("--read-unread"),Oe=parseInt(z.find(e=>e.startsWith("--max="))?.split("=")[1]||"50");function Es(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 _s(e,t){console.log(`
|
|
55
55
|
\u2550\u2550 PHASE 1: Inbox Scan \u2550\u2550
|
|
56
56
|
`);let o=Ke(),n=o.lastIncrementalSync?new Date(o.lastIncrementalSync):null,s=new Map,r=new Map,i=new Map,a=0,l=0,c=0,f=0,d=0,u=-1,m=0;for(;;){l++;let $=await Xt(e),C=0;for(let p of $){if(!p.name||s.has(p.name))continue;s.set(p.name,p),C++,p.unread&&i.set(p.name,p);let w=t[p.name],k=Es(p,w);k&&(!$t&&p.unread?a++:r.set(p.name,{card:p,reason:k})),w&&(k||(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(vt?console.log(` Round #${l}: +${C} new | Total: ${s.size} | Need reading: ${r.size}`):process.stdout.write(`\r Scanned: ${s.size} conversations (page #${l}, ${r.size} need reading)`),C===0){if(d++,d>=3){console.log(`
|
|
57
57
|
3 rounds with no new items \u2014 end of visible list.`);break}}else d=0;if(!bt){if(r.size===f){if(c++,c>=3){console.log(`
|
|
58
58
|
Caught up \u2014 3 screens with no new threads to read.`);break}}else c=0;f=r.size}let g=await tt(e);if(g){if(g.scrollTop===u){if(m++,m>=3){console.log(`
|
|
59
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 E(2e3)}vt||process.stdout.write(`
|
|
60
60
|
`);let S=new Date().toISOString(),y=0;for(let[$,{card:C}]of r)t[$]||(t[$]={profileUrl:null,lastSynced:S,lastMessageBy:C.lastMessageBy,lastMessagePreview:C.lastMessagePreview||"",lastMessageTime:C.lastMessageTime||"",unread:C.unread||!1,conversationStatus:C.lastMessageBy==="them"?"unanswered_by_us":"awaiting_reply",messages:[]},y++);let v=0;for(let[$,C]of i)t[$]||(t[$]={profileUrl:null,lastSynced:S,lastMessageBy:C.lastMessageBy,lastMessagePreview:C.lastMessagePreview||"",lastMessageTime:C.lastMessageTime||"",unread:!0,conversationStatus:C.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}`),!$t&&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:C}of r.values())$[C]=($[C]||0)+1;for(let[C,g]of Object.entries($))console.log(` ${C}: ${g}`)}if(!L){let $=Ke();$.lastIncrementalSync=new Date().toISOString(),bt&&($.lastFullSync=$.lastIncrementalSync),Ut($),
|
|
61
|
+
Inbox scan complete:`),console.log(` Total conversations seen: ${s.size}`),console.log(` New conversations: ${y}`),console.log(` Need reading/re-reading: ${r.size}`),!$t&&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:C}of r.values())$[C]=($[C]||0)+1;for(let[C,g]of Object.entries($))console.log(` ${C}: ${g}`)}if(!L){let $=Ke();$.lastIncrementalSync=new Date().toISOString(),bt&&($.lastFullSync=$.lastIncrementalSync),Ut($),X(t)}return{allCards:s,toReRead:r,unreadCards:i}}async function Rs(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=[],l=[],c=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 it(y,u)}catch(C){if(
|
|
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(To,{waitUntil:"domcontentloaded",timeout:6e4})}catch(d){if(!ce(d))throw d;await Je(e,"Phase 2 inbox reset")}await E(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=[],l=[],c=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 it(y,u)}catch(C){if(ce(C))throw C;console.log(` searchAndOpenThread threw: ${(C.message||"").substring(0,80)}`)}if(!v.verified){console.log(" Sidebar fallback (scroll-to-find)...");try{v=await Zt(e.page,u)}catch(C){if(ce(C))throw C;console.log(` Fallback threw: ${(C.message||"").substring(0,80)}`)}}if(!v.verified){console.log(" Could not open thread."),c++;try{await Z(e.page)}catch{}continue}let $=await nt(y);if($&&$.length>0){if(!st($,u,l)){console.log(" Skipped \u2014 stale data."),c++,await Z(y);continue}let g=await ot(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=rt(p.messages,$),p.lastSynced=new Date().toISOString(),g&&!p.profileUrl&&(p.profileUrl=g);let k=[...p.messages].reverse().find(I=>!["draft","confirmed","rejected","needs_review"].includes(I.status));if(k?.sender){let I=(M||"").toLowerCase().split(/\s+/).filter(V=>V.length>1),Se=k.sender.toLowerCase();p.lastMessageBy=I.some(V=>Se.includes(V))?"me":"them"}else p.lastMessageBy=m.lastMessageBy;p.lastMessagePreview=m.lastMessagePreview||p.lastMessagePreview,p.lastMessageTime=m.lastMessageTime||p.lastMessageTime,p.unread=!1,p._cardSynced=!0,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}),l=[...new Set($.map(I=>I.sender).filter(I=>I&&I!=="Unknown"))]}else console.log(" No messages extracted."),c++;await Z(y),await E(500)}catch(v){if(!ce(v))throw v;f++,n&&n.add(u),await Je(e,`Phase 2 thread ${d+1}/${r} (${u})`)}}return console.log(`
|
|
65
65
|
Phase 2 complete: ${a.length} read, ${c} 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 Ds(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{lt(r,s,o)}catch(l){console.warn(` [!] Enrichment failed for ${s}: ${l.message}`)}delete r._cardSynced;let i=r.profileUrl,a=null;if(i&&(a=o[i]||null),!a){let l=Object.entries(o).filter(([,c])=>c.name===s);l.length===1&&(i||(i=l[0][0],r.profileUrl=i),a=l[0][1])}!a&&i&&(Ft(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||(Me(o),X(e)),console.log(` Enriched ${t.length} conversations.`),Ao(e)}function Ao(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,l=a===r?i:r,c=new Set((a.conv.messages||[]).map(f=>`${f.text?.slice(0,80)}|${f.time}`));for(let f of l.conv.messages||[]){let d=`${f.text?.slice(0,80)}|${f.time}`;c.has(d)||(a.conv.messages.push(f),c.add(d))}a.conv.messages.sort((f,d)=>(f.time||"").localeCompare(d.time||"")),l.conv.notes&&!a.conv.notes&&(a.conv.notes=l.conv.notes),l.conv.priority&&!a.conv.priority&&(a.conv.priority=l.conv.priority),l.conv.categorySource==="manual"&&a.conv.categorySource!=="manual"&&(a.conv.category=l.conv.category,a.conv.categorySource="manual"),(l.conv.lastSynced||"")>(a.conv.lastSynced||"")&&(a.conv.lastSynced=l.conv.lastSynced),delete e[l.name],console.log(` Merged: "${l.name}" \u2192 "${a.name}"`),n++}n>0&&(L||X(e),console.log(` ${n} duplicates merged.`))}function Os(){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{lt(u,d,t)}catch(m){console.warn(` [!] Enrichment failed for ${d}: ${m.message}`)}n++}Ao(e),L||
|
|
70
|
-
People enriched: ${c}`),console.log(` People skipped (have conversation or not connected): ${f}`),console.log(` People total: ${l.length}`),Ct(e,null,null)}function Ct(e,t,o,n={}){let s=Object.entries(e),r=
|
|
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{lt(u,d,t)}catch(m){console.warn(` [!] Enrichment failed for ${d}: ${m.message}`)}n++}Ao(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)),l=Object.entries(t),c=0,f=0;for(let[d,u]of l){if(a.has(d)){f++;continue}if(u.name&&i.has(u.name)){f++;continue}if(u.connectionStatus!=="connected"){f++;continue}so(u,d),c++}L||Me(t),console.log(`
|
|
70
|
+
People enriched: ${c}`),console.log(` People skipped (have conversation or not connected): ${f}`),console.log(` People total: ${l.length}`),Ct(e,null,null)}function Ct(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
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 l=s.length,c=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
73
|
Database:`),console.log(` Total conversations: ${l}`),console.log(` With messages: ${c}`),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(`
|
|
@@ -75,40 +75,40 @@ ${n.join(`
|
|
|
75
75
|
Statuses:`);for(let[h,T]of Object.entries(y).sort((A,D)=>D[1]-A[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
76
|
Categories:`);for(let[h,T]of Object.entries(v).sort((A,D)=>D[1]-A[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
77
|
Actions Needed:`);for(let[h,T]of Object.entries($).sort((A,D)=>D[1]-A[1]))console.log(` ${h}: ${T}`)}let C=new Map;for(let[h,T]of s){if(!T.profileUrl)continue;let A=T.profileUrl.toLowerCase().replace(/\/$/,"");C.has(A)||C.set(A,[]),C.get(A).push(h)}let g=[...C.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])=>
|
|
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[A,D]of h.slice(0,T)){let
|
|
81
|
+
`);let T=30;for(let[A,D]of h.slice(0,T)){let P=e[A],Ie=P?.category?` [${P.category}]`:"",we=D.lastMessageTime||P?.lastMessageTime||"",He=(D.lastMessagePreview||"").slice(0,80),xe=P?.summary,be=P?.personContext?.title?`${P.personContext.title}${P.personContext.company?` at ${P.personContext.company}`:""}`:null;console.log(` ${A}${Ie}${we?` \u2014 ${we}`:""}`),He&&console.log(` "${He}"`),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(([,A])=>
|
|
84
|
+
`)}if(w.length>0){let h=w.filter(([,A])=>ye(A)===j.CONFIRMED),T=w.filter(([,A])=>{let D=ye(A);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 k=p.filter(([,h])=>h.actionNeeded==="schedule_call"),R=p.filter(([,h])=>h.actionNeeded!=="schedule_call");if(R.sort((h,T)=>{let A={high:0,medium:1,low:2}[h[1].suggestedPriority]??3,D={high:0,medium:1,low:2}[T[1].suggestedPriority]??3;return A-D}),k.length>0){console.log(` SCHEDULE CALL (${k.length}):
|
|
85
85
|
`);for(let[h,T]of k){let A=T.category||"",D=T.summary||"";console.log(` ${h}${A?` [${A}]`:""} \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,A]of R.slice(0,h)){let D=A.category||"",
|
|
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,A]of
|
|
90
|
-
... and ${
|
|
86
|
+
`);let h=20;for(let[T,A]of R.slice(0,h)){let D=A.category||"",P=A.suggestedPriority?`[${A.suggestedPriority}]`:"",Ie=(A.autoTags||[]).slice(0,4).join(", "),we=A.summary||"",xe=[...A.messages||[]].reverse().find(be=>be.sender!==M&&be.sender!=="Unknown")?.text?.slice(0,80)||A.lastMessagePreview?.slice(0,80)||"";console.log(` ${P} ${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)),Se=new Set(Object.values(e).map(h=>h.profileUrl).filter(Boolean)),V=Object.entries(r).filter(([h,T])=>T.connectionStatus==="connected"&&T.personActionNeeded==="initiate_contact"&&!Se.has(h)&&!(T.name&&I.has(T.name))).sort((h,T)=>{let A={high:0,medium:1,low:2}[h[1].personPriority]??3,D={high:0,medium:1,low:2}[T[1].personPriority]??3;return A!==D?A-D:(T[1].connectionDate||"").localeCompare(h[1].connectionDate||"")});if(V.length>0){console.log(`
|
|
88
|
+
INITIATE CONTACT (${V.length}):
|
|
89
|
+
`);let h=20;for(let[T,A]of V.slice(0,h)){let D=A.personPriority?`[${A.personPriority.toUpperCase()}]`:"",P=A.personSummary||A.title||"";console.log(` ${D} ${A.name||"Unknown"} \u2014 ${P}`)}V.length>h&&console.log(`
|
|
90
|
+
... and ${V.length-h} more`)}let kt=s.filter(([,h])=>h.conversationStatus==="awaiting_reply");kt.length>0&&console.log(`
|
|
91
91
|
BALL IN THEIR COURT: ${kt.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
94
|
`)}async function Is(e,t){console.log(`
|
|
95
95
|
\u2550\u2550 BACKFILL: Reading conversations with 0 messages \u2550\u2550
|
|
96
|
-
`);let o=Object.entries(t).filter(([,l])=>!l.messages||l.messages.length===0).map(([l,c])=>({name:l,preview:c.lastMessagePreview||"",lastBy:c.lastMessageBy}));if(o.length===0)return console.log(" All conversations already have messages."),[];o.sort((l,c)=>l.lastBy==="them"&&c.lastBy!=="them"?-1:l.lastBy!=="them"&&c.lastBy==="them"?1:0);let n=Math.min(o.length,
|
|
97
|
-
`);let s=[],r=0,i=0,a=[];for(let l=0;l<n;l++){let{name:c}=o[l];console.log(` [${l+1}/${n}] ${c}`);let f=e.page;try{if(await At(f),!(await it(f,c)).verified){console.log(" Not found or thread didn't load."),r++,await
|
|
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 xs(e){let t=Lt(),o=Object.values(e),n=o.filter(l=>l.sources?.includes("recruiters-from-jobs")&&!l.connectionStatus).length,s=o.filter(l=>l.connectionStatus==="modal_failed").length,r=n+s,i=o.filter(l=>l.connectionStatus==="connected"&&l.connectionMethod).length,a=o.filter(l=>l.connectionStatus==="pending"||l.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 js(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?
|
|
96
|
+
`);let o=Object.entries(t).filter(([,l])=>!l.messages||l.messages.length===0).map(([l,c])=>({name:l,preview:c.lastMessagePreview||"",lastBy:c.lastMessageBy}));if(o.length===0)return console.log(" All conversations already have messages."),[];o.sort((l,c)=>l.lastBy==="them"&&c.lastBy!=="them"?-1:l.lastBy!=="them"&&c.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 l=0;l<n;l++){let{name:c}=o[l];console.log(` [${l+1}/${n}] ${c}`);let f=e.page;try{if(await At(f),!(await it(f,c)).verified){console.log(" Not found or thread didn't load."),r++,await Z(f),await E(500);continue}let u=await nt(f);if(!u||u.length===0){console.log(" No messages extracted."),r++,await Z(f),await E(500);continue}if(!st(u,c,a)){console.log(" Skipped \u2014 stale data."),r++,await Z(f),await E(500);continue}let S=await ot(f),y=t[c]||{profileUrl:null,lastSynced:new Date().toISOString(),lastMessageBy:null,lastMessagePreview:"",lastMessageTime:"",unread:!1,conversationStatus:null,messages:[]};y.messages=rt(y.messages,u),y.lastSynced=new Date().toISOString(),S&&(y.profileUrl=S),t[c]=y,L||X(t),console.log(` \u2713 ${y.messages.length} msgs${S?" +url":""}`),s.push({name:c}),a=[...new Set(u.map(v=>v.sender).filter(v=>v&&v!=="Unknown"))],await Z(f),await E(500)}catch(d){if(!ce(d))throw d;i++,await Je(e,`Backfill thread ${l+1}/${n} (${c})`)}}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 xs(e){let t=Lt(),o=Object.values(e),n=o.filter(l=>l.sources?.includes("recruiters-from-jobs")&&!l.connectionStatus).length,s=o.filter(l=>l.connectionStatus==="modal_failed").length,r=n+s,i=o.filter(l=>l.connectionStatus==="connected"&&l.connectionMethod).length,a=o.filter(l=>l.connectionStatus==="pending"||l.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 js(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 Ms(){let e=Wt(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)}`:"",l=n.recruiterUrl?" \u{1F464}":"";console.log(` ${String(t+1).padStart(2)}. [${String(o).padStart(2)}] ${s} @ ${r}${i}${a}${l}`)}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 Ns(){let e=Ht();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 Ps(){let e=Bt();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 Fs(){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
100
|
`),console.log(co()),console.log(""),xs(t),console.log(""),js(t),console.log(""),Ns(),console.log(""),Ms(),console.log(""),Ps(),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,k]=g[p],R=k.category?` [${k.category}]`:"",
|
|
102
|
-
`);for(let[g,p]of l){let w=p.followUpDate?` [due ${p.followUpDate}]`:" [no date]";console.log(` \u26A1 ${g}${w} \u2014 ${p.followUpNote}`)}for(let[g,p]of c.slice(0,5))console.log(` \u{1F4C5} ${g} [${p.followUpDate}] \u2014 ${p.followUpNote}`);c.length>5&&console.log(` ... +${c.length-5} more scheduled`),console.log()}let f=g=>g.actionNeeded&&g.actionNeeded!=="none"&&(!g.reviewedAtPreview||
|
|
103
|
-
`);for(let p=0;p<g.length;p++){let[w,k]=g[p],R=k.priority||k.suggestedPriority||"",
|
|
101
|
+
`);for(let p=0;p<g.length;p++){let[w,k]=g[p],R=k.category?` [${k.category}]`:"",I=k.summary||(k.personContext?.title?`${k.personContext.title}${k.personContext.company?` at ${k.personContext.company}`:""}`:null)||k.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),l=a.filter(([,g])=>!g.followUpDate||g.followUpDate<=i),c=a.filter(([,g])=>g.followUpDate&&g.followUpDate>i);if(a.length>0){console.log(` FOLLOW-UPS (${l.length} due, ${c.length} scheduled):
|
|
102
|
+
`);for(let[g,p]of l){let w=p.followUpDate?` [due ${p.followUpDate}]`:" [no date]";console.log(` \u26A1 ${g}${w} \u2014 ${p.followUpNote}`)}for(let[g,p]of c.slice(0,5))console.log(` \u{1F4C5} ${g} [${p.followUpDate}] \u2014 ${p.followUpNote}`);c.length>5&&console.log(` ... +${c.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,k]=g[p],R=k.priority||k.suggestedPriority||"",I=k.category?` [${k.category}]`:"",Se=k.summary||"";console.log(` ${p+1}. ${R?`[${R}] `:""}${w}${I} \u2014 ${Se}`)}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,k={high:0,medium:1,low:2}[p[1].suggestedPriority]??3;return w-k});if(u.length>0){let g=u.slice(0,5);console.log(` CALLS TO SCHEDULE (${u.length}):
|
|
104
104
|
`);for(let p=0;p<g.length;p++){let[w,k]=g[p],R=k.summary||k.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,k]=g[p],R=k.category?` [${k.category}]`:"",
|
|
106
|
-
`);for(let p=0;p<g.length;p++){let[w,k]=g[p],R=k.category?` [${k.category}]`:"",
|
|
107
|
-
`);for(let p=0;p<g.length;p++){let[,w]=g[p],k=w.personPriority?`[${w.personPriority.toUpperCase()}]`:"",R=w.connectionDate?Ls(w.connectionDate):"",
|
|
105
|
+
`);for(let p=0;p<g.length;p++){let[w,k]=g[p],R=k.category?` [${k.category}]`:"",I=k.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,k]=g[p],R=k.category?` [${k.category}]`:"",I=k.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,k={high:0,medium:1,low:2}[p[1].personPriority]??3;return w!==k?w-k:(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],k=w.personPriority?`[${w.personPriority.toUpperCase()}]`:"",R=w.connectionDate?Ls(w.connectionDate):"",I=w.title?`${w.title}${w.company?` at ${w.company}`:""}`:w.company||"";console.log(` ${p+1}. ${k} ${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 k=ye(w);return k===j.DRAFT||k===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,C=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, ${C} 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 Ls(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 Us(){let e=
|
|
109
|
+
`)}function Ls(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 Us(){let e=Te();Ct(e,null,null)}async function Bs(){if(console.log(`
|
|
110
110
|
LinkedIn Sync-All
|
|
111
|
-
`),ko){Fs();return}if(vo){let i=
|
|
112
|
-
\u26A0 Phase 1 CDP dead (attempt ${l}/${wt}). Recovering and restarting Phase 1...`),await
|
|
113
|
-
CDP page recoveries this run: ${
|
|
114
|
-
Error:`,i.message),vt&&console.error(i.stack),
|
|
111
|
+
`),ko){Fs();return}if(vo){let i=Ue("sync-all","report");Us(),he(i,"success");return}if(Co){let i=Ue("sync-all","enrich");try{Os(),he(i,"success")}catch(a){throw he(i,"error",null,a.message),a}return}let e=$o?"backfill":bt?"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 Kt(),r={browser:s.browser,page:s.page};try{await et(r.page);let i=null,a=[];if($o)a=await Is(r,n);else{let l=0;for(;;)try{i=await _s(r.page,n);break}catch(u){if(!ce(u))throw u;if(l++,l>=wt)throw new Error(`Phase 1 failed ${wt} times on CDP death \u2014 giving up. Last: ${u.message}`);console.log(`
|
|
112
|
+
\u26A0 Phase 1 CDP dead (attempt ${l}/${wt}). Recovering and restarting Phase 1...`),await Je(r,`Phase 1 attempt ${l}`)}let c=new Set;a=await Rs(r,i.toReRead,n,c),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)||c.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((l,c)=>l+(c.newMessages||0),0),a.length>0&&Ds(n,a),Ct(n,i,a,{unreadCards:i?.unreadCards,readUnread:$t}),ue.pageRecoveries>0&&console.log(`
|
|
113
|
+
CDP page recoveries this run: ${ue.pageRecoveries}`),L||Xe(),he(t,"success",o)}catch(i){console.error(`
|
|
114
|
+
Error:`,i.message),vt&&console.error(i.stack),he(t,"error",o,i.message)}finally{if(!L&&!vo&&!Co&&!ko)try{Xe()}catch{}await r.browser.close()}}Bs();
|
package/ilml-plugin.json
CHANGED
package/package.json
CHANGED