ilml-plugin-linkedin 1.3.1 → 1.5.0
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 +17 -0
- package/dist/applyQueue.mjs +3 -3
- package/dist/daily.mjs +8 -8
- package/dist/funnelRunner.mjs +10 -10
- package/dist/messageBot.mjs +1 -1
- package/dist/run.mjs +38 -36
- package/dist/scout.mjs +1 -1
- package/dist/syncAll.mjs +1 -1
- package/dist/viewers.mjs +1 -1
- package/dist/visit.mjs +1 -1
- package/ilml-plugin.json +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.5.0
|
|
4
|
+
- **Easy Apply now fills optional fields too, not just required ones.** Until now we only answered fields that LinkedIn flagged with a validation error — i.e. required ones. Optional fields (cover letter, salary expectation, LinkedIn URL, portfolio, notice period, etc.) silently went unanswered, which is exactly the kind of small signal that makes a candidate look less interested. Each step of the form is now scanned proactively: any field that's not required, not pre-filled by LinkedIn from your profile, and still empty gets routed through Lifebot for an answer. If Lifebot returns nothing or an answer that doesn't match the available options, the field stays empty — no breakage, just no answer logged.
|
|
5
|
+
- **Pre-filled values stay untouched.** When LinkedIn pulls phone / email / name from your profile we leave it alone, even if the field is technically optional. The pre-filled answer goes into the stats as-is (`wasPreFilled: true`), counted as your answer because it came from your profile.
|
|
6
|
+
- **Form Q&A log now records `wasOptional` and `isRequired` per field**, so the upcoming FAQ/canonicalization step (cluster recurring questions, let the user pick canonical answers, feed them back to Lifebot) can tell required from optional and surface optional answers the user may want to override. `isRequired` is always written explicitly on new entries (`true` OR `false`); legacy entries from previous versions simply omit the flag and are read as "unknown" — no migration needed, old `application-questions.json` files keep working as-is.
|
|
7
|
+
- Internal: new `fillOptionalFields(page, fields)` runs in `processNavigation` between the City/CV special-case block and `fillErrorsWithAnswers`. Three new setters in `run.mjs` (`setRadioForLabel`, `setSelectForLabel`, `setCheckboxesForLabel`) mirror the existing `setAnswerForLabel`, with the same shadow-DOM fallback (`#interop-outlet`). `getAnsweredLabels` now records `wasPreFilled` for checkbox and file types too, not just input/radio/select. Failures inside `fillOptionalFields` are caught and logged — they never abort the application.
|
|
8
|
+
- Heads-up: each application now makes more Lifebot calls (roughly +1 per empty optional field). Form completion is slightly slower; nothing else changes about the queue, daily limits, or output formats.
|
|
9
|
+
|
|
10
|
+
## 1.4.0
|
|
11
|
+
- **Apply phase no longer crashes on Puppeteer hangs.** When Chrome's CDP (DevTools Protocol) channel goes dead — the most common failure mode on long sessions, where `Runtime.callFunctionOn timed out` previously aborted the run with `Apply phase crashed: ... Increase the 'protocolTimeout' setting` — the runner now detects it via a fast liveness probe and transparently recovers. Recovery escalates: close the dead page and open a fresh one (cookies preserved, navigates back to the search URL, continues applying) → if that keeps failing, full browser restart → if even that fails, exit with a clear actionable error. Jobs in the queue aren't lost; the run keeps going.
|
|
12
|
+
- **Clearer error messages when authentication expires.** Two distinct, easy-to-confuse expiries are now detected separately and printed with the *correct* command:
|
|
13
|
+
- LinkedIn cookies expired → `Run: ilml linkedin login`
|
|
14
|
+
- iLiveMyLife platform token expired (~once a month) → `Run: ilml login` (different layer)
|
|
15
|
+
- **Recruiter funnel no longer runs on a dead browser.** If apply ended due to an unrecoverable CDP failure, funnel is skipped with a clear log line and the recruiter list is preserved on disk for the next run, instead of throwing a stack trace at the user.
|
|
16
|
+
- **Session summary** now reports recovery activity when it kicks in: `Page recoveries (CDP): N`, `Browser restarts: M`. Lines only appear when nonzero, so healthy runs are unchanged.
|
|
17
|
+
- Internal: new `src/cdpRecovery.mjs` (`livenessProbe`, `livenessProbeStrict`, `browserAlive`, `recoverPage`, `relaunchBrowser`, `isCdpDeadError`, `withTimeout`). Two-strike liveness probe (3s × 2 with 5s gap) keeps false positives near zero — healthy LinkedIn pages answer `evaluate(()=>1)` in <50 ms even when network is slow or spinners are running. Recovery limits: 3 consecutive page recoveries, then escalates to browser restart (cap 2). Browser-level liveness check before page recovery skips the 5-minute `newPage()` hang when the whole Chrome process is gone.
|
|
18
|
+
- Internal: `ERROR_RULES` in `run.mjs` simplified — CDP-class errors (`ProtocolError`, `Target closed`, `Runtime.callFunctionOn timed out`, etc.) are now intercepted before `classifyError` and re-thrown to `iterateAllPages` for centralized page recovery. No more `closeJobApplicationPopup`-on-a-dead-page hangs that themselves cost another `protocolTimeout`. Queue mode (`--job-urls`) gets the same recovery path
|
|
19
|
+
|
|
3
20
|
## 1.3.1
|
|
4
21
|
- **`ilml linkedin warm-scan` now actually works** — `warmScan.mjs` was previously only reachable through `npm run warm-scan` in the dev repo. Wired through to the published plugin: added `warm-scan` to `ilml-plugin.json` commands, `warmScan.mjs` to the build entry points, and `target-companies.json` to the npm tarball. The script now finds `target-companies.json` in three locations, in order: `<DATA_DIR>/target-companies.json` (user override), `<package-root>/target-companies.json` (shipped default for prod), source dir (dev mode)
|
|
5
22
|
- README polish — leads with the killer differentiators that were buried before: AI drafts that go through user review with re-check on push, auto-triaged inbox classification, `--skip-unread` sync that preserves LinkedIn unread badges. Daily-commands table descriptions tightened (especially `today`, `enrich`, `daily`, which were vague or wrong); added `apply-queue` and `warm-scan` rows; new "Common routines" section with morning-triage / active-search / passive-pipeline / targeted-networking patterns
|
package/dist/applyQueue.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
var mt=Object.defineProperty;var Ie=(t,e)=>()=>(t&&(e=t(t=0)),e);var $e=(t,e)=>{for(var o in e)mt(t,o,{get:e[o],enumerable:!0})};var ze={};$e(ze,{addEncounter:()=>xe,canSendConnection:()=>Je,commitDataSnapshot:()=>Ge,default:()=>wt,getAllApplications:()=>Ve,getPersonStatus:()=>Le,getQuestionStats:()=>Qe,getQuotaStatus:()=>Ne,getRecentConnections:()=>ee,linkPersonToJob:()=>_e,loadApplicationQuestions:()=>de,loadConversations:()=>
|
|
1
|
+
var mt=Object.defineProperty;var Ie=(t,e)=>()=>(t&&(e=t(t=0)),e);var $e=(t,e)=>{for(var o in e)mt(t,o,{get:e[o],enumerable:!0})};var ze={};$e(ze,{addEncounter:()=>xe,canSendConnection:()=>Je,commitDataSnapshot:()=>Ge,default:()=>wt,getAllApplications:()=>Ve,getPersonStatus:()=>Le,getQuestionStats:()=>Qe,getQuotaStatus:()=>Ne,getRecentConnections:()=>ee,linkPersonToJob:()=>_e,loadApplicationQuestions:()=>de,loadConversations:()=>Re,loadJobs:()=>H,loadPeople:()=>O,loadQuota:()=>Z,loadSyncState:()=>Ue,logApplicationQuestions:()=>Be,migrateFromDailyFiles:()=>Me,recordConnectionSent:()=>ke,resetCaches:()=>yt,saveConversations:()=>Ae,saveJobs:()=>X,savePeople:()=>A,saveQuota:()=>fe,saveSyncState:()=>Pe,updateConnectionStatus:()=>Fe,upsertJob:()=>Ee,upsertPerson:()=>pe});import"dotenv/config";import*as d from"fs";import*as h from"path";import{fileURLToPath as ht}from"url";import{execSync as ie}from"child_process";function yt(){L=null,F=null,D=null,C=null,k=null,T=null}function y(){d.existsSync(S)||d.mkdirSync(S,{recursive:!0})}function R(t,e){let o=JSON.stringify(e,null,2),r=t+".tmp";d.writeFileSync(r,o);for(let n=0;n<3;n++)try{d.renameSync(r,t);return}catch(s){if(n<2&&(s.code==="EPERM"||s.code==="EBUSY")){let i=Date.now();for(;Date.now()-i<200;);continue}throw s}}function U(t,e){if(!d.existsSync(t))return null;try{let o=JSON.parse(d.readFileSync(t,"utf-8"));try{d.copyFileSync(t,t+".backup")}catch{}return o}catch(o){console.error(`[!] ${e} is corrupted: ${o.message}`),console.error(` File: ${h.resolve(t)}`);let r=t+".backup";if(d.existsSync(r))try{let n=JSON.parse(d.readFileSync(r,"utf-8"));console.error(` Restored from ${r}`);try{d.copyFileSync(r,t)}catch{}return n}catch{console.error(" Backup also corrupted!")}console.error(` Starting with empty data. Old file preserved as ${t}.corrupted`);try{d.copyFileSync(t,t+".corrupted")}catch{}return null}}function J(){return new Date().toISOString().split("T")[0]}function O(){return L||(y(),L=U(ce,"people.json")||{},L)}function A(t){y(),L=t;try{R(ce,t)}catch(e){console.error(`[!] Failed to save people.json: ${e.message}`),console.error(` Path: ${h.resolve(ce)}`)}}function H(){return F||(y(),F=U(le,"jobs.json")||{},F)}function X(t){y(),F=t;try{R(le,t)}catch(e){console.error(`[!] Failed to save jobs.json: ${e.message}`),console.error(` Path: ${h.resolve(le)}`)}}function Re(){return C||(y(),C=U(K,"conversations.json")||{},jt(C),C)}function jt(t){let e=0;for(let o of Object.values(t)){if(!(o.draftStatus!==void 0||o.draftPreparedAt!==void 0))continue;let n=(o.messages||[]).find(s=>s.status==="draft");n&&o.draftStatus&&o.draftStatus!=="pending"&&(n.status=o.draftStatus),delete o.draftStatus,delete o.draftPreparedAt,e++}if(e>0){console.log(` [migration] Moved draft state from conv to msg.status for ${e} conversations`);try{R(K,t)}catch{}}}function Ae(t){y(),C=t;try{R(K,t)}catch(e){console.error(`[!] Failed to save conversations.json: ${e.message}`),console.error(` Path: ${h.resolve(K)}`)}}function pe(t,e={}){if(!t)return null;let o=O(),r=J(),n=new Date().toISOString(),s=o[t];if(s){s.seenCount=(s.seenCount||1)+1,s.lastSeen=r,s.lastUpdated=n,e.source&&!s.sources?.includes(e.source)&&(s.sources=s.sources||[],s.sources.push(e.source));for(let i of ue)e[i]!=null&&(s[i]=e[i])}else o[t]={name:e.name||null,title:e.title||null,location:e.location||null,company:e.company||null,connectionDegree:e.connectionDegree||null,connectionsCount:e.connectionsCount||null,mutualConnections:e.mutualConnections||null,photoUrl:e.photoUrl||null,hasVerifiedBadge:e.hasVerifiedBadge||!1,firstSeen:r,lastSeen:r,seenCount:1,sources:e.source?[e.source]:[],connectionStatus:null,connectionDate:null,connectionMethod:null,messageSent:null,jobIds:[],lastUpdated:n};return A(o),o[t]}function Ee(t,e={}){if(!t)return null;let o=H(),r=J(),n=o[t];if(n){let s=["title","company","companyUrl","location","salary","url","applied","appliedDate","recruiterUrl","description","techStack","jobTags","seniority","employmentType","industries","jobFunction","postedTime","applicantCount","companySize","workplaceType","contractType","isEasyApply","applyType","scoutedAt","lastSeen"];for(let i of s)e[i]!=null&&(n[i]=e[i])}else o[t]={title:e.title||null,company:e.company||null,location:e.location||null,salary:e.salary||null,url:e.url||null,applied:e.applied||!1,appliedDate:e.appliedDate||r,recruiterUrl:e.recruiterUrl||null,firstSeen:r};return X(o),o[t]}function _e(t,e){if(!t||!e)return;let o=O(),r=o[t];r&&(r.jobIds||(r.jobIds=[]),r.jobIds.includes(e)||(r.jobIds.push(e),r.lastUpdated=new Date().toISOString(),A(o)))}function xe(t,e){if(!t||!e)return;let o=O(),r=o[t];if(!r)return;r.encounters||(r.encounters=[]);let s={date:J(),source:e.source||"unknown",action:e.action||"unknown"};e.jobId&&(s.jobId=String(e.jobId)),e.jobTitle&&(s.jobTitle=e.jobTitle),e.jobCompany&&(s.jobCompany=e.jobCompany),!r.encounters.some(a=>a.date===s.date&&a.source===s.source&&a.action===s.action&&(a.jobId||"")===(s.jobId||""))&&(r.encounters.push(s),r.lastUpdated=new Date().toISOString(),A(o))}function Le(t){return t&&O()[t]?.connectionStatus||null}function Fe(t,e,o={}){if(!t)return;let r=O(),n=r[t];if(n||(pe(t,o.profileInfo||{}),n=r[t]),!!n){if(n.connectionStatus=e,n.connectionDate=J(),n.connectionMethod=o.method||n.connectionMethod,n.messageSent=o.messageSent||n.messageSent,n.lastUpdated=new Date().toISOString(),o.profileInfo)for(let s of ue)o.profileInfo[s]!=null&&(n[s]=o.profileInfo[s]);A(r)}}function Z(){return D||(y(),D=U(Te,"quota.json"),(!D||!D.connections)&&(D={connections:[]}),D)}function fe(t){y(),D=t;try{R(Te,t)}catch(e){console.error(`[!] Failed to save quota.json: ${e.message}`)}}function ke(t,e="unknown"){let o=Z(),r=new Date;o.connections.push({date:r.toISOString().split("T")[0],time:r.toTimeString().split(" ")[0],profileUrl:t,source:e}),fe(o)}function ee(){let t=Z(),e=new Date;e.setDate(e.getDate()-7);let o=e.toISOString().split("T")[0];return t.connections.filter(r=>r.date>=o)}function Je(){let t=ee(),e=J(),o=t.filter(u=>u.date===e).length,r=t.length,n=o>=N,s=r>=M,i=!n&&!s,a={allowed:i,daily:{sent:o,max:N},weekly:{sent:r,max:M}};return i||(a.reason=n?`Daily limit reached (${o}/${N})`:`Weekly limit reached (${r}/${M})`),a}function Ne(){let t=ee(),e=J(),o=t.filter(n=>n.date===e).length,r=t.length;return{daily:{sent:o,max:N,remaining:N-o},weekly:{sent:r,max:M,remaining:M-r}}}function bt(t){if(!t)return null;let e=t.match(/jobs\/view\/(\d+)/);return e?e[1]:null}function Me(){y();let t=d.readdirSync(S).filter(s=>s.endsWith(".json")&&s!=="people.json"&&s!=="jobs.json"&&s!=="quota.json");if(t.length===0)return console.log("No daily files found to migrate."),{people:0,jobs:0};console.log(`
|
|
2
2
|
Migrating from ${t.length} daily files...
|
|
3
|
-
`);let e=O(),o=H(),r=0,n=0;t.sort();for(let s of t){let i=h.join(S,s),a;try{a=JSON.parse(d.readFileSync(i,"utf-8"))}catch(c){console.log(` Skipping ${s}: ${c.message}`);continue}let u=a.date||s.match(/\d{4}-\d{2}-\d{2}/)?.[0]||null,p=a.source||s.split("-").slice(0,-1).join("-").replace(/-\d{4}$/,"")||"unknown";if(!a.profiles||!Array.isArray(a.profiles)){console.log(` Skipping ${s}: no profiles array`);continue}console.log(` ${s}: ${a.profiles.length} profiles`);for(let c of a.profiles){let m=c.profileUrl;if(!m)continue;let l=e[m];if(l){l.seenCount=(l.seenCount||1)+1,u&&(!l.lastSeen||u>l.lastSeen)&&(l.lastSeen=u),u&&(!l.firstSeen||u<l.firstSeen)&&(l.firstSeen=u),p&&!l.sources?.includes(p)&&(l.sources=l.sources||[],l.sources.push(p));for(let f of ue)c[f]!=null&&(l[f]=c[f]);c.status&&c.processed&&(l.connectionStatus=c.status,l.connectionDate=u,c.funnelResult&&(l.connectionMethod=c.funnelResult.method||l.connectionMethod,l.messageSent=c.funnelResult.message||l.messageSent)),l.lastUpdated=new Date().toISOString()}else e[m]={name:c.name||null,title:c.title||null,location:c.location||null,company:c.company||null,connectionDegree:c.connectionDegree||null,connectionsCount:c.connectionsCount||null,mutualConnections:c.mutualConnections||null,photoUrl:c.photoUrl||null,hasVerifiedBadge:c.hasVerifiedBadge||!1,firstSeen:u,lastSeen:u,seenCount:1,sources:p?[p]:[],connectionStatus:c.processed&&c.status||null,connectionDate:c.processed?u:null,connectionMethod:c.funnelResult?.method||null,messageSent:c.funnelResult?.message||null,jobIds:[],lastUpdated:new Date().toISOString()},r++;if(c.fromJob?.url){let f=bt(c.fromJob.url);f&&(e[m].jobIds||(e[m].jobIds=[]),e[m].jobIds.includes(f)||e[m].jobIds.push(f),o[f]?o[f].recruiterUrl||(o[f].recruiterUrl=m):(o[f]={title:c.fromJob.title||null,company:c.fromJob.company||null,location:null,salary:null,url:c.fromJob.url,applied:!0,appliedDate:u,recruiterUrl:m,firstSeen:u},n++))}}}return
|
|
4
|
-
Migration complete!`),console.log(` People: ${Object.keys(e).length} total (${r} new)`),console.log(` Jobs: ${Object.keys(o).length} total (${n} new)`),{people:Object.keys(e).length,jobs:Object.keys(o).length}}function Ue(){return k||(y(),k=U(Ce,"sync-state.json")||{},k)}function Pe(t){y(),k=t;try{A(Ce,t)}catch(e){console.error(`[!] Failed to save sync-state.json: ${e.message}`)}}function de(){if(T)return T;y();try{d.existsSync(ae)&&(T=JSON.parse(d.readFileSync(ae,"utf-8")))}catch{}return T||(T={}),T}function qe(t){return Array.isArray(t.applications)}function Ve(){let t=de();if(qe(t))return t.applications;let e=[];for(let o of Object.values(t))o.applications&&e.push(...o.applications);return e}function Be(t){if(!t||!t.questions||t.questions.length===0)return;let e=de(),o=t.jobId||"unknown",n={applicationId:`${o}_${Date.now()}`,date:new Date().toISOString(),result:t.result||"submitted",formPages:t.formPages||null,duration:t.duration||null,retries:t.retries||0,failedField:t.failedField||null,questionCount:t.questions.length,questions:t.questions.map(s=>{let i={type:s.type||"input",question:s.labelText||s.question||"",answer:s.answer||""};return s.options&&(s.options.length<=vt?i.options=s.options:i.optionsOmitted=!0),s.page&&(i.page=s.page),s.wasPreFilled&&(i.wasPreFilled=!0),s.wasRetry&&(i.wasRetry=!0),i})};qe(e)?(n.jobId=o,n.jobTitle=t.jobTitle||null,n.jobCompany=t.jobCompany||null,n.jobUrl=t.jobUrl||null,e.applications.push(n)):(e[o]||(e[o]={applications:[]}),e[o].applications.push(n));try{A(ae,e)}catch(s){console.error(`[!] Failed to save application-questions.json: ${s.message}`)}}function Qe(){let t=Ve(),e=new Map;for(let s of t)for(let i of s.questions||[]){let a=i.question.toLowerCase().trim();if(!a)continue;e.has(a)||e.set(a,{question:i.question,count:0,answers:new Map});let u=e.get(a);u.count++;let p=(i.answer||"").trim();u.answers.set(p,(u.answers.get(p)||0)+1)}let o=[...e.values()].sort((s,i)=>i.count-s.count),r=o.slice(0,15).map(s=>({question:s.question,count:s.count,topAnswer:[...s.answers.entries()].sort((i,a)=>a[1]-i[1])[0]?.[0]||""})),n=o.filter(s=>s.answers.size>1&&s.count>=3).map(s=>({question:s.question,count:s.count,answers:[...s.answers.entries()].sort((i,a)=>a[1]-i[1]).map(([i,a])=>`${i} (${a}x)`)}));return{totalApplications:t.length,uniqueQuestions:e.size,topQuestions:r,inconsistent:n}}function Ge(t){if(d.existsSync(h.join(S,".git")))try{let e={cwd:S,stdio:"pipe",timeout:1e4};ie("git add conversations.json people.json jobs.json quota.json sync-state.json run-log.json application-questions.json",e);try{ie("git diff --cached --quiet",e);return}catch{}let o=(t||`sync ${new Date().toISOString().slice(0,16)}`).replace(/"/g,'\\"');ie(`git commit -m "${o}"`,e)}catch(e){process.env.VERBOSE&&console.log(` [git] snapshot skipped: ${e.message?.slice(0,80)}`)}}var gt,St,S,ce,le,Te,K,Ce,ae,N,M,L,F,D,C,k,T,ue,vt,wt,me=Ie(()=>{gt=ht(import.meta.url),St=h.dirname(h.dirname(gt)),S=process.env.DATA_DIR?h.resolve(process.env.DATA_DIR,"collected-profiles"):h.join(St,"collected-profiles"),ce=h.join(S,"people.json"),le=h.join(S,"jobs.json"),Te=h.join(S,"quota.json"),K=h.join(S,"conversations.json"),Ce=h.join(S,"sync-state.json"),ae=h.join(S,"application-questions.json"),N=20,M=100,L=null,F=null,D=null,C=null,k=null,T=null;ue=["name","title","location","company","connectionDegree","connectionsCount","mutualConnections","photoUrl","hasVerifiedBadge"];vt=50;wt={loadPeople:O,savePeople:R,loadJobs:H,saveJobs:X,loadConversations:Ae,saveConversations:Re,upsertPerson:pe,upsertJob:Ee,linkPersonToJob:_e,addEncounter:xe,getPersonStatus:Le,updateConnectionStatus:Fe,migrateFromDailyFiles:Me,loadQuota:Z,saveQuota:fe,recordConnectionSent:ke,canSendConnection:Je,getRecentConnections:ee,getQuotaStatus:Ne,loadSyncState:Ue,saveSyncState:Pe,commitDataSnapshot:Ge,logApplicationQuestions:Be,getQuestionStats:Qe}});var et={};$e(et,{getJobDecision:()=>Ct,getLastVisit:()=>_t,getScoutStats:()=>xt,loadCompanies:()=>Ze,loadDiscoveredPeople:()=>ge,loadJobDecisions:()=>Y,loadScoutedJobs:()=>W,loadVisitLog:()=>te,recordVisit:()=>Et,resetCaches:()=>$t,saveCompany:()=>Rt,saveDiscoveredPerson:()=>At,saveScoutedJob:()=>he,setJobDecision:()=>Tt});import"dotenv/config";import*as v from"fs";import*as j from"path";import{fileURLToPath as Ot}from"url";function Q(){v.existsSync(_)||v.mkdirSync(_,{recursive:!0})}function G(t,e){let o=JSON.stringify(e,null,2),r=t+".tmp";v.writeFileSync(r,o);for(let n=0;n<3;n++)try{v.renameSync(r,t);return}catch(s){if(n<2&&(s.code==="EPERM"||s.code==="EBUSY")){let i=Date.now();for(;Date.now()-i<200;);continue}throw s}}function z(t){try{if(v.existsSync(t))return JSON.parse(v.readFileSync(t,"utf-8"))}catch{}return null}function $t(){P=null,q=null,V=null,B=null,E=null}function W(){return P||(P=z(We)||{},P)}function he(t,e){if(!t)return;Q();let o=W(),r=new Date().toISOString().slice(0,10);if(o[t]){for(let[n,s]of Object.entries(e))s!=null&&(o[t][n]=s);o[t].lastSeen=r}else o[t]={...e,scoutedAt:r,lastSeen:r};try{G(We,o)}catch(n){console.error(`[!] Failed to save scouted-jobs.json: ${n.message}`)}}function Y(){return q||(q=z(Xe)||{},q)}function Tt(t,e,o={}){if(!t||!e)return;Q();let r=Y(),n=new Date().toISOString().slice(0,10);r[t]?(r[t].status=e,r[t].updatedAt=n,o.reason&&(r[t].reason=o.reason),o.score!=null&&(r[t].score=o.score),o.source&&(r[t].source=o.source)):r[t]={status:e,decidedAt:n,updatedAt:n,reason:o.reason||null,score:o.score??null,source:o.source||null};try{G(Xe,r)}catch(s){console.error(`[!] Failed to save job-decisions.json: ${s.message}`)}}function Ct(t){return Y()[t]||null}function ge(){return V||(V=z(Ye)||{},V)}function At(t,e){if(!t)return;Q();let o=ge(),r=new Date().toISOString().slice(0,10);if(o[t]){let n=o[t];e.name&&(n.name=e.name),e.title&&(n.title=e.title),e.company&&(n.company=e.company),e.location&&(n.location=e.location),e.role&&(n.role=e.role),e.isConnected!=null&&(n.isConnected=e.isConnected),e.jobId&&(n.jobIds||(n.jobIds=[]),n.jobIds.includes(e.jobId)||n.jobIds.push(e.jobId)),n.lastSeen=r,n.seenCount=(n.seenCount||1)+1}else o[t]={name:e.name||null,title:e.title||null,company:e.company||null,location:e.location||null,role:e.role||null,source:e.source||"scout",isConnected:e.isConnected||!1,discoveredAt:r,lastSeen:r,seenCount:1,jobIds:e.jobId?[e.jobId]:[]};try{G(Ye,o)}catch(n){console.error(`[!] Failed to save discovered-people.json: ${n.message}`)}}function Ze(){return B||(B=z(He)||{},B)}function Rt(t,e){if(!t)return;Q();let o=Ze(),r=new Date().toISOString().slice(0,10);if(o[t]){let n=o[t];e.name&&(n.name=e.name),e.about&&(n.about=e.about),e.size&&(n.size=e.size),e.industries&&(n.industries=e.industries),e.jobId&&(n.jobIds||(n.jobIds=[]),n.jobIds.includes(e.jobId)||n.jobIds.push(e.jobId)),e.recruiterUrl&&(n.recruiterUrls||(n.recruiterUrls=[]),n.recruiterUrls.includes(e.recruiterUrl)||n.recruiterUrls.push(e.recruiterUrl)),n.lastSeen=r,n.jobCount=n.jobIds?.length||0}else o[t]={name:e.name||null,about:e.about||null,size:e.size||null,industries:e.industries||null,discoveredAt:r,lastSeen:r,jobIds:e.jobId?[e.jobId]:[],recruiterUrls:e.recruiterUrl?[e.recruiterUrl]:[],jobCount:e.jobId?1:0};try{G(He,o)}catch(n){console.error(`[!] Failed to save companies.json: ${n.message}`)}}function te(){return E||(E=z(Ke)||{visits:[]},Array.isArray(E.visits)||(E.visits=[]),E)}function Et(t,e={}){if(!t)return;Q();let o=te();o.visits.push({date:new Date().toISOString(),profileUrl:t,name:e.name||null,source:e.source||"manual"});let r=Date.now()-90*24*60*60*1e3;o.visits=o.visits.filter(n=>new Date(n.date).getTime()>r);try{G(Ke,o)}catch(n){console.error(`[!] Failed to save visit-log.json: ${n.message}`)}}function _t(t){let e=te();for(let o=e.visits.length-1;o>=0;o--)if(e.visits[o].profileUrl===t)return e.visits[o].date;return null}function xt(){let t=W(),e=ge(),o=Object.values(t),r=Object.values(e),n={};for(let l of o)l.company&&(n[l.company]=(n[l.company]||0)+1);let s=Object.entries(n).sort((l,f)=>f[1]-l[1]).slice(0,5).map(([l,f])=>`${l} (${f})`),i={};for(let l of o)for(let f of l.techStack||[])i[f]=(i[f]||0)+1;let a=Object.entries(i).sort((l,f)=>f[1]-l[1]).slice(0,8).map(([l,f])=>`${l} (${f})`),u=te(),p=new Date().toISOString().slice(0,10),c=u.visits.filter(l=>l.date.startsWith(p)).length,m=new Set(u.visits.map(l=>l.profileUrl)).size;return{totalJobs:o.length,totalRecruiters:r.length,topCompanies:s,topTech:a,totalVisited:m,todayVisited:c,lastScoutDate:o.length>0?o.sort((l,f)=>(f.scoutedAt||"").localeCompare(l.scoutedAt||""))[0]?.scoutedAt:null}}var Dt,It,_,We,Ye,Ke,He,Xe,P,q,V,B,E,oe=Ie(()=>{Dt=Ot(import.meta.url),It=j.dirname(j.dirname(Dt)),_=process.env.DATA_DIR?j.resolve(process.env.DATA_DIR,"market-research"):j.join(It,"market-research"),We=j.join(_,"scouted-jobs.json"),Ye=j.join(_,"discovered-people.json"),Ke=j.join(_,"visit-log.json"),He=j.join(_,"companies.json"),Xe=j.join(_,"job-decisions.json");P=null;q=null;V=null;B=null;E=null});import"dotenv/config";import*as $ from"fs";import*as x from"path";import{fileURLToPath as to}from"url";import{execFileSync as oo}from"child_process";me();oe();var Lt=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"]),Ft=new Set(["iot","machine learning","ai","data engineering","spark","go","rust","kotlin","next.js","devops","sre"]),tt=["vancouver","british columbia"],kt=["canada","montreal","quebec","alberta","calgary","ottawa"],Jt=["toronto","ontario"],Nt=["india","pakistan","nigeria","philippines"];function Mt(t,e={}){let o=0,r=(t.techStack||[]).map(c=>c.toLowerCase()),n=0,s=0;for(let c of r)Lt.has(c)&&n++,Ft.has(c)&&s++;if(o+=Math.min(n*6,24),o+=Math.min(s*3,6),t.salary){let c=Ut(t.salary);c&&(c>=15e4?o+=20:c>=12e4?o+=15:c>=1e5?o+=10:c>=8e4?o+=5:c>=100&&c<1e3?o+=15:c>=70&&c<1e3&&(o+=10))}let i=(t.location||"").toLowerCase(),a=/remote/i.test(i)||t.workplaceType==="remote";Nt.some(c=>i.includes(c))?o-=30:a&&/united states|usa|\bus\b/i.test(i)?o+=25:a&&tt.some(c=>i.includes(c))?o+=22:a?o+=20:tt.some(c=>i.includes(c))?o+=18:kt.some(c=>i.includes(c))?o+=10:Jt.some(c=>i.includes(c))&&(o+=5),t.isEasyApply&&(o+=5);let u=(t.seniority||"").toLowerCase();u.includes("senior")||u.includes("lead")||u.includes("staff")?o+=10:u.includes("mid")?o+=5:(u.includes("entry")||u.includes("intern"))&&(o-=10),t.recruiterUrl&&(e.conversationRecruiters?.has(t.recruiterUrl)?o+=20:e.connectedRecruiters?.has(t.recruiterUrl)?o+=15:o+=3);let p=new Set(t.jobTags||[]);return p.has("managerial")?o+=8:p.has("senior-ic")&&(o+=5),p.has("junior")&&(o-=15),p.has("consultant")&&(o-=3),Math.max(0,Math.min(100,o))}function Se(t=10,e={}){let{easyApplyOnly:o=!0}=e,r=W(),n=O(),s=new Set,i=new Set;for(let[m,l]of Object.entries(n))l.connectionStatus==="connected"&&s.add(m);let a={connectedRecruiters:s,conversationRecruiters:i},u=Y(),p=new Set(["applied","skipped","expired","archived"]);return Object.entries(r).filter(([m,l])=>{if(typeof l!="object"||o&&!l.isEasyApply||l.applied)return!1;let f=u[m];return!(f&&p.has(f.status))}).map(([m,l])=>({jobId:m,score:Mt(l,a),job:l})).sort((m,l)=>{if(l.score!==m.score)return l.score-m.score;let f=m.job.scoutedAt||m.job.postedTime||"";return(l.job.scoutedAt||l.job.postedTime||"").localeCompare(f)}).slice(0,t)}function Ut(t){if(!t)return null;let e=t.match(/[\$£€CAD\s]*([\d,]+(?:\.\d+)?)\s*[kK]?/g);if(!e||e.length===0)return null;let o=e.map(r=>{let n=parseFloat(r.replace(/[^\d.]/g,""));return r.toLowerCase().includes("k")&&(n*=1e3),n}).filter(r=>r>0);return o.length>=2?(o[0]+o[1])/2:o.length===1?o[0]:null}oe();import"dotenv/config";import*as w from"fs";import*as I from"path";import{fileURLToPath as Gt}from"url";import"dotenv/config";import{createGraphClient as Pt,resolveToken as qt}from"@ilivemylife/graph-sdk";import{config as Vt}from"dotenv";Vt();var ot=qt()||process.env.TOKEN;ot||(console.error(`
|
|
3
|
+
`);let e=O(),o=H(),r=0,n=0;t.sort();for(let s of t){let i=h.join(S,s),a;try{a=JSON.parse(d.readFileSync(i,"utf-8"))}catch(c){console.log(` Skipping ${s}: ${c.message}`);continue}let u=a.date||s.match(/\d{4}-\d{2}-\d{2}/)?.[0]||null,p=a.source||s.split("-").slice(0,-1).join("-").replace(/-\d{4}$/,"")||"unknown";if(!a.profiles||!Array.isArray(a.profiles)){console.log(` Skipping ${s}: no profiles array`);continue}console.log(` ${s}: ${a.profiles.length} profiles`);for(let c of a.profiles){let m=c.profileUrl;if(!m)continue;let l=e[m];if(l){l.seenCount=(l.seenCount||1)+1,u&&(!l.lastSeen||u>l.lastSeen)&&(l.lastSeen=u),u&&(!l.firstSeen||u<l.firstSeen)&&(l.firstSeen=u),p&&!l.sources?.includes(p)&&(l.sources=l.sources||[],l.sources.push(p));for(let f of ue)c[f]!=null&&(l[f]=c[f]);c.status&&c.processed&&(l.connectionStatus=c.status,l.connectionDate=u,c.funnelResult&&(l.connectionMethod=c.funnelResult.method||l.connectionMethod,l.messageSent=c.funnelResult.message||l.messageSent)),l.lastUpdated=new Date().toISOString()}else e[m]={name:c.name||null,title:c.title||null,location:c.location||null,company:c.company||null,connectionDegree:c.connectionDegree||null,connectionsCount:c.connectionsCount||null,mutualConnections:c.mutualConnections||null,photoUrl:c.photoUrl||null,hasVerifiedBadge:c.hasVerifiedBadge||!1,firstSeen:u,lastSeen:u,seenCount:1,sources:p?[p]:[],connectionStatus:c.processed&&c.status||null,connectionDate:c.processed?u:null,connectionMethod:c.funnelResult?.method||null,messageSent:c.funnelResult?.message||null,jobIds:[],lastUpdated:new Date().toISOString()},r++;if(c.fromJob?.url){let f=bt(c.fromJob.url);f&&(e[m].jobIds||(e[m].jobIds=[]),e[m].jobIds.includes(f)||e[m].jobIds.push(f),o[f]?o[f].recruiterUrl||(o[f].recruiterUrl=m):(o[f]={title:c.fromJob.title||null,company:c.fromJob.company||null,location:null,salary:null,url:c.fromJob.url,applied:!0,appliedDate:u,recruiterUrl:m,firstSeen:u},n++))}}}return A(e),X(o),console.log(`
|
|
4
|
+
Migration complete!`),console.log(` People: ${Object.keys(e).length} total (${r} new)`),console.log(` Jobs: ${Object.keys(o).length} total (${n} new)`),{people:Object.keys(e).length,jobs:Object.keys(o).length}}function Ue(){return k||(y(),k=U(Ce,"sync-state.json")||{},k)}function Pe(t){y(),k=t;try{R(Ce,t)}catch(e){console.error(`[!] Failed to save sync-state.json: ${e.message}`)}}function de(){if(T)return T;y();try{d.existsSync(ae)&&(T=JSON.parse(d.readFileSync(ae,"utf-8")))}catch{}return T||(T={}),T}function qe(t){return Array.isArray(t.applications)}function Ve(){let t=de();if(qe(t))return t.applications;let e=[];for(let o of Object.values(t))o.applications&&e.push(...o.applications);return e}function Be(t){if(!t||!t.questions||t.questions.length===0)return;let e=de(),o=t.jobId||"unknown",n={applicationId:`${o}_${Date.now()}`,date:new Date().toISOString(),result:t.result||"submitted",formPages:t.formPages||null,duration:t.duration||null,retries:t.retries||0,failedField:t.failedField||null,questionCount:t.questions.length,questions:t.questions.map(s=>{let i={type:s.type||"input",question:s.labelText||s.question||"",answer:s.answer||""};return s.options&&(s.options.length<=vt?i.options=s.options:i.optionsOmitted=!0),s.page&&(i.page=s.page),s.wasPreFilled&&(i.wasPreFilled=!0),s.wasRetry&&(i.wasRetry=!0),typeof s.isRequired=="boolean"&&(i.isRequired=s.isRequired),s.wasOptional&&(i.wasOptional=!0),i})};qe(e)?(n.jobId=o,n.jobTitle=t.jobTitle||null,n.jobCompany=t.jobCompany||null,n.jobUrl=t.jobUrl||null,e.applications.push(n)):(e[o]||(e[o]={applications:[]}),e[o].applications.push(n));try{R(ae,e)}catch(s){console.error(`[!] Failed to save application-questions.json: ${s.message}`)}}function Qe(){let t=Ve(),e=new Map;for(let s of t)for(let i of s.questions||[]){let a=i.question.toLowerCase().trim();if(!a)continue;e.has(a)||e.set(a,{question:i.question,count:0,answers:new Map});let u=e.get(a);u.count++;let p=(i.answer||"").trim();u.answers.set(p,(u.answers.get(p)||0)+1)}let o=[...e.values()].sort((s,i)=>i.count-s.count),r=o.slice(0,15).map(s=>({question:s.question,count:s.count,topAnswer:[...s.answers.entries()].sort((i,a)=>a[1]-i[1])[0]?.[0]||""})),n=o.filter(s=>s.answers.size>1&&s.count>=3).map(s=>({question:s.question,count:s.count,answers:[...s.answers.entries()].sort((i,a)=>a[1]-i[1]).map(([i,a])=>`${i} (${a}x)`)}));return{totalApplications:t.length,uniqueQuestions:e.size,topQuestions:r,inconsistent:n}}function Ge(t){if(d.existsSync(h.join(S,".git")))try{let e={cwd:S,stdio:"pipe",timeout:1e4};ie("git add conversations.json people.json jobs.json quota.json sync-state.json run-log.json application-questions.json",e);try{ie("git diff --cached --quiet",e);return}catch{}let o=(t||`sync ${new Date().toISOString().slice(0,16)}`).replace(/"/g,'\\"');ie(`git commit -m "${o}"`,e)}catch(e){process.env.VERBOSE&&console.log(` [git] snapshot skipped: ${e.message?.slice(0,80)}`)}}var gt,St,S,ce,le,Te,K,Ce,ae,N,M,L,F,D,C,k,T,ue,vt,wt,me=Ie(()=>{gt=ht(import.meta.url),St=h.dirname(h.dirname(gt)),S=process.env.DATA_DIR?h.resolve(process.env.DATA_DIR,"collected-profiles"):h.join(St,"collected-profiles"),ce=h.join(S,"people.json"),le=h.join(S,"jobs.json"),Te=h.join(S,"quota.json"),K=h.join(S,"conversations.json"),Ce=h.join(S,"sync-state.json"),ae=h.join(S,"application-questions.json"),N=20,M=100,L=null,F=null,D=null,C=null,k=null,T=null;ue=["name","title","location","company","connectionDegree","connectionsCount","mutualConnections","photoUrl","hasVerifiedBadge"];vt=50;wt={loadPeople:O,savePeople:A,loadJobs:H,saveJobs:X,loadConversations:Re,saveConversations:Ae,upsertPerson:pe,upsertJob:Ee,linkPersonToJob:_e,addEncounter:xe,getPersonStatus:Le,updateConnectionStatus:Fe,migrateFromDailyFiles:Me,loadQuota:Z,saveQuota:fe,recordConnectionSent:ke,canSendConnection:Je,getRecentConnections:ee,getQuotaStatus:Ne,loadSyncState:Ue,saveSyncState:Pe,commitDataSnapshot:Ge,logApplicationQuestions:Be,getQuestionStats:Qe}});var et={};$e(et,{getJobDecision:()=>Ct,getLastVisit:()=>_t,getScoutStats:()=>xt,loadCompanies:()=>Ze,loadDiscoveredPeople:()=>ge,loadJobDecisions:()=>Y,loadScoutedJobs:()=>W,loadVisitLog:()=>te,recordVisit:()=>Et,resetCaches:()=>$t,saveCompany:()=>At,saveDiscoveredPerson:()=>Rt,saveScoutedJob:()=>he,setJobDecision:()=>Tt});import"dotenv/config";import*as v from"fs";import*as j from"path";import{fileURLToPath as Ot}from"url";function Q(){v.existsSync(_)||v.mkdirSync(_,{recursive:!0})}function G(t,e){let o=JSON.stringify(e,null,2),r=t+".tmp";v.writeFileSync(r,o);for(let n=0;n<3;n++)try{v.renameSync(r,t);return}catch(s){if(n<2&&(s.code==="EPERM"||s.code==="EBUSY")){let i=Date.now();for(;Date.now()-i<200;);continue}throw s}}function z(t){try{if(v.existsSync(t))return JSON.parse(v.readFileSync(t,"utf-8"))}catch{}return null}function $t(){P=null,q=null,V=null,B=null,E=null}function W(){return P||(P=z(We)||{},P)}function he(t,e){if(!t)return;Q();let o=W(),r=new Date().toISOString().slice(0,10);if(o[t]){for(let[n,s]of Object.entries(e))s!=null&&(o[t][n]=s);o[t].lastSeen=r}else o[t]={...e,scoutedAt:r,lastSeen:r};try{G(We,o)}catch(n){console.error(`[!] Failed to save scouted-jobs.json: ${n.message}`)}}function Y(){return q||(q=z(Xe)||{},q)}function Tt(t,e,o={}){if(!t||!e)return;Q();let r=Y(),n=new Date().toISOString().slice(0,10);r[t]?(r[t].status=e,r[t].updatedAt=n,o.reason&&(r[t].reason=o.reason),o.score!=null&&(r[t].score=o.score),o.source&&(r[t].source=o.source)):r[t]={status:e,decidedAt:n,updatedAt:n,reason:o.reason||null,score:o.score??null,source:o.source||null};try{G(Xe,r)}catch(s){console.error(`[!] Failed to save job-decisions.json: ${s.message}`)}}function Ct(t){return Y()[t]||null}function ge(){return V||(V=z(Ye)||{},V)}function Rt(t,e){if(!t)return;Q();let o=ge(),r=new Date().toISOString().slice(0,10);if(o[t]){let n=o[t];e.name&&(n.name=e.name),e.title&&(n.title=e.title),e.company&&(n.company=e.company),e.location&&(n.location=e.location),e.role&&(n.role=e.role),e.isConnected!=null&&(n.isConnected=e.isConnected),e.jobId&&(n.jobIds||(n.jobIds=[]),n.jobIds.includes(e.jobId)||n.jobIds.push(e.jobId)),n.lastSeen=r,n.seenCount=(n.seenCount||1)+1}else o[t]={name:e.name||null,title:e.title||null,company:e.company||null,location:e.location||null,role:e.role||null,source:e.source||"scout",isConnected:e.isConnected||!1,discoveredAt:r,lastSeen:r,seenCount:1,jobIds:e.jobId?[e.jobId]:[]};try{G(Ye,o)}catch(n){console.error(`[!] Failed to save discovered-people.json: ${n.message}`)}}function Ze(){return B||(B=z(He)||{},B)}function At(t,e){if(!t)return;Q();let o=Ze(),r=new Date().toISOString().slice(0,10);if(o[t]){let n=o[t];e.name&&(n.name=e.name),e.about&&(n.about=e.about),e.size&&(n.size=e.size),e.industries&&(n.industries=e.industries),e.jobId&&(n.jobIds||(n.jobIds=[]),n.jobIds.includes(e.jobId)||n.jobIds.push(e.jobId)),e.recruiterUrl&&(n.recruiterUrls||(n.recruiterUrls=[]),n.recruiterUrls.includes(e.recruiterUrl)||n.recruiterUrls.push(e.recruiterUrl)),n.lastSeen=r,n.jobCount=n.jobIds?.length||0}else o[t]={name:e.name||null,about:e.about||null,size:e.size||null,industries:e.industries||null,discoveredAt:r,lastSeen:r,jobIds:e.jobId?[e.jobId]:[],recruiterUrls:e.recruiterUrl?[e.recruiterUrl]:[],jobCount:e.jobId?1:0};try{G(He,o)}catch(n){console.error(`[!] Failed to save companies.json: ${n.message}`)}}function te(){return E||(E=z(Ke)||{visits:[]},Array.isArray(E.visits)||(E.visits=[]),E)}function Et(t,e={}){if(!t)return;Q();let o=te();o.visits.push({date:new Date().toISOString(),profileUrl:t,name:e.name||null,source:e.source||"manual"});let r=Date.now()-90*24*60*60*1e3;o.visits=o.visits.filter(n=>new Date(n.date).getTime()>r);try{G(Ke,o)}catch(n){console.error(`[!] Failed to save visit-log.json: ${n.message}`)}}function _t(t){let e=te();for(let o=e.visits.length-1;o>=0;o--)if(e.visits[o].profileUrl===t)return e.visits[o].date;return null}function xt(){let t=W(),e=ge(),o=Object.values(t),r=Object.values(e),n={};for(let l of o)l.company&&(n[l.company]=(n[l.company]||0)+1);let s=Object.entries(n).sort((l,f)=>f[1]-l[1]).slice(0,5).map(([l,f])=>`${l} (${f})`),i={};for(let l of o)for(let f of l.techStack||[])i[f]=(i[f]||0)+1;let a=Object.entries(i).sort((l,f)=>f[1]-l[1]).slice(0,8).map(([l,f])=>`${l} (${f})`),u=te(),p=new Date().toISOString().slice(0,10),c=u.visits.filter(l=>l.date.startsWith(p)).length,m=new Set(u.visits.map(l=>l.profileUrl)).size;return{totalJobs:o.length,totalRecruiters:r.length,topCompanies:s,topTech:a,totalVisited:m,todayVisited:c,lastScoutDate:o.length>0?o.sort((l,f)=>(f.scoutedAt||"").localeCompare(l.scoutedAt||""))[0]?.scoutedAt:null}}var Dt,It,_,We,Ye,Ke,He,Xe,P,q,V,B,E,oe=Ie(()=>{Dt=Ot(import.meta.url),It=j.dirname(j.dirname(Dt)),_=process.env.DATA_DIR?j.resolve(process.env.DATA_DIR,"market-research"):j.join(It,"market-research"),We=j.join(_,"scouted-jobs.json"),Ye=j.join(_,"discovered-people.json"),Ke=j.join(_,"visit-log.json"),He=j.join(_,"companies.json"),Xe=j.join(_,"job-decisions.json");P=null;q=null;V=null;B=null;E=null});import"dotenv/config";import*as $ from"fs";import*as x from"path";import{fileURLToPath as to}from"url";import{execFileSync as oo}from"child_process";me();oe();var Lt=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"]),Ft=new Set(["iot","machine learning","ai","data engineering","spark","go","rust","kotlin","next.js","devops","sre"]),tt=["vancouver","british columbia"],kt=["canada","montreal","quebec","alberta","calgary","ottawa"],Jt=["toronto","ontario"],Nt=["india","pakistan","nigeria","philippines"];function Mt(t,e={}){let o=0,r=(t.techStack||[]).map(c=>c.toLowerCase()),n=0,s=0;for(let c of r)Lt.has(c)&&n++,Ft.has(c)&&s++;if(o+=Math.min(n*6,24),o+=Math.min(s*3,6),t.salary){let c=Ut(t.salary);c&&(c>=15e4?o+=20:c>=12e4?o+=15:c>=1e5?o+=10:c>=8e4?o+=5:c>=100&&c<1e3?o+=15:c>=70&&c<1e3&&(o+=10))}let i=(t.location||"").toLowerCase(),a=/remote/i.test(i)||t.workplaceType==="remote";Nt.some(c=>i.includes(c))?o-=30:a&&/united states|usa|\bus\b/i.test(i)?o+=25:a&&tt.some(c=>i.includes(c))?o+=22:a?o+=20:tt.some(c=>i.includes(c))?o+=18:kt.some(c=>i.includes(c))?o+=10:Jt.some(c=>i.includes(c))&&(o+=5),t.isEasyApply&&(o+=5);let u=(t.seniority||"").toLowerCase();u.includes("senior")||u.includes("lead")||u.includes("staff")?o+=10:u.includes("mid")?o+=5:(u.includes("entry")||u.includes("intern"))&&(o-=10),t.recruiterUrl&&(e.conversationRecruiters?.has(t.recruiterUrl)?o+=20:e.connectedRecruiters?.has(t.recruiterUrl)?o+=15:o+=3);let p=new Set(t.jobTags||[]);return p.has("managerial")?o+=8:p.has("senior-ic")&&(o+=5),p.has("junior")&&(o-=15),p.has("consultant")&&(o-=3),Math.max(0,Math.min(100,o))}function Se(t=10,e={}){let{easyApplyOnly:o=!0}=e,r=W(),n=O(),s=new Set,i=new Set;for(let[m,l]of Object.entries(n))l.connectionStatus==="connected"&&s.add(m);let a={connectedRecruiters:s,conversationRecruiters:i},u=Y(),p=new Set(["applied","skipped","expired","archived"]);return Object.entries(r).filter(([m,l])=>{if(typeof l!="object"||o&&!l.isEasyApply||l.applied)return!1;let f=u[m];return!(f&&p.has(f.status))}).map(([m,l])=>({jobId:m,score:Mt(l,a),job:l})).sort((m,l)=>{if(l.score!==m.score)return l.score-m.score;let f=m.job.scoutedAt||m.job.postedTime||"";return(l.job.scoutedAt||l.job.postedTime||"").localeCompare(f)}).slice(0,t)}function Ut(t){if(!t)return null;let e=t.match(/[\$£€CAD\s]*([\d,]+(?:\.\d+)?)\s*[kK]?/g);if(!e||e.length===0)return null;let o=e.map(r=>{let n=parseFloat(r.replace(/[^\d.]/g,""));return r.toLowerCase().includes("k")&&(n*=1e3),n}).filter(r=>r>0);return o.length>=2?(o[0]+o[1])/2:o.length===1?o[0]:null}oe();import"dotenv/config";import*as w from"fs";import*as I from"path";import{fileURLToPath as Gt}from"url";import"dotenv/config";import{createGraphClient as Pt,resolveToken as qt}from"@ilivemylife/graph-sdk";import{config as Vt}from"dotenv";Vt();var ot=qt()||process.env.TOKEN;ot||(console.error(`
|
|
5
5
|
[FATAL] No iLiveMyLife token found.`),console.error(" The bot uses Lifebot AI to decide which jobs to apply for"),console.error(` and how to fill application forms. It cannot work without it.
|
|
6
6
|
`),console.error(" Easiest way:"),console.error(" 1. npm install -g @ilivemylife/graph-sdk"),console.error(` 2. ilml login your@email.com yourpassword
|
|
7
7
|
`),console.error(` Or add ILML_TOKEN=... to .env
|
package/dist/daily.mjs
CHANGED
|
@@ -1,22 +1,22 @@
|
|
|
1
|
-
var ht=Object.defineProperty;var De=(t,e)=>()=>(t&&(e=t(t=0)),e);var Ie=(t,e)=>{for(var o in e)ht(t,o,{get:e[o],enumerable:!0})};var Ge={};Ie(Ge,{addEncounter:()=>Ne,canSendConnection:()=>ee,commitDataSnapshot:()=>We,default:()=>xt,getAllApplications:()=>Qe,getPersonStatus:()=>Me,getQuestionStats:()=>ze,getQuotaStatus:()=>te,getRecentConnections:()=>Z,linkPersonToJob:()=>ke,loadApplicationQuestions:()=>me,loadConversations:()=>xe,loadJobs:()=>K,loadPeople:()=>
|
|
1
|
+
var ht=Object.defineProperty;var De=(t,e)=>()=>(t&&(e=t(t=0)),e);var Ie=(t,e)=>{for(var o in e)ht(t,o,{get:e[o],enumerable:!0})};var Ge={};Ie(Ge,{addEncounter:()=>Ne,canSendConnection:()=>ee,commitDataSnapshot:()=>We,default:()=>xt,getAllApplications:()=>Qe,getPersonStatus:()=>Me,getQuestionStats:()=>ze,getQuotaStatus:()=>te,getRecentConnections:()=>Z,linkPersonToJob:()=>ke,loadApplicationQuestions:()=>me,loadConversations:()=>xe,loadJobs:()=>K,loadPeople:()=>O,loadQuota:()=>X,loadSyncState:()=>qe,logApplicationQuestions:()=>Ye,migrateFromDailyFiles:()=>Ue,recordConnectionSent:()=>Pe,resetCaches:()=>Ct,saveConversations:()=>Fe,saveJobs:()=>H,savePeople:()=>A,saveQuota:()=>de,saveSyncState:()=>Ve,updateConnectionStatus:()=>Je,upsertJob:()=>Le,upsertPerson:()=>fe});import"dotenv/config";import*as d from"fs";import*as m from"path";import{fileURLToPath as $t}from"url";import{execSync as ce}from"child_process";function Ct(){x=null,F=null,I=null,T=null,L=null,R=null}function S(){d.existsSync(h)||d.mkdirSync(h,{recursive:!0})}function C(t,e){let o=JSON.stringify(e,null,2),s=t+".tmp";d.writeFileSync(s,o);for(let n=0;n<3;n++)try{d.renameSync(s,t);return}catch(r){if(n<2&&(r.code==="EPERM"||r.code==="EBUSY")){let i=Date.now();for(;Date.now()-i<200;);continue}throw r}}function P(t,e){if(!d.existsSync(t))return null;try{let o=JSON.parse(d.readFileSync(t,"utf-8"));try{d.copyFileSync(t,t+".backup")}catch{}return o}catch(o){console.error(`[!] ${e} is corrupted: ${o.message}`),console.error(` File: ${m.resolve(t)}`);let s=t+".backup";if(d.existsSync(s))try{let n=JSON.parse(d.readFileSync(s,"utf-8"));console.error(` Restored from ${s}`);try{d.copyFileSync(s,t)}catch{}return n}catch{console.error(" Backup also corrupted!")}console.error(` Starting with empty data. Old file preserved as ${t}.corrupted`);try{d.copyFileSync(t,t+".corrupted")}catch{}return null}}function k(){return new Date().toISOString().split("T")[0]}function O(){return x||(S(),x=P(le,"people.json")||{},x)}function A(t){S(),x=t;try{C(le,t)}catch(e){console.error(`[!] Failed to save people.json: ${e.message}`),console.error(` Path: ${m.resolve(le)}`)}}function K(){return F||(S(),F=P(ae,"jobs.json")||{},F)}function H(t){S(),F=t;try{C(ae,t)}catch(e){console.error(`[!] Failed to save jobs.json: ${e.message}`),console.error(` Path: ${m.resolve(ae)}`)}}function xe(){return T||(S(),T=P(G,"conversations.json")||{},At(T),T)}function At(t){let e=0;for(let o of Object.values(t)){if(!(o.draftStatus!==void 0||o.draftPreparedAt!==void 0))continue;let n=(o.messages||[]).find(r=>r.status==="draft");n&&o.draftStatus&&o.draftStatus!=="pending"&&(n.status=o.draftStatus),delete o.draftStatus,delete o.draftPreparedAt,e++}if(e>0){console.log(` [migration] Moved draft state from conv to msg.status for ${e} conversations`);try{C(G,t)}catch{}}}function Fe(t){S(),T=t;try{C(G,t)}catch(e){console.error(`[!] Failed to save conversations.json: ${e.message}`),console.error(` Path: ${m.resolve(G)}`)}}function fe(t,e={}){if(!t)return null;let o=O(),s=k(),n=new Date().toISOString(),r=o[t];if(r){r.seenCount=(r.seenCount||1)+1,r.lastSeen=s,r.lastUpdated=n,e.source&&!r.sources?.includes(e.source)&&(r.sources=r.sources||[],r.sources.push(e.source));for(let i of pe)e[i]!=null&&(r[i]=e[i])}else o[t]={name:e.name||null,title:e.title||null,location:e.location||null,company:e.company||null,connectionDegree:e.connectionDegree||null,connectionsCount:e.connectionsCount||null,mutualConnections:e.mutualConnections||null,photoUrl:e.photoUrl||null,hasVerifiedBadge:e.hasVerifiedBadge||!1,firstSeen:s,lastSeen:s,seenCount:1,sources:e.source?[e.source]:[],connectionStatus:null,connectionDate:null,connectionMethod:null,messageSent:null,jobIds:[],lastUpdated:n};return A(o),o[t]}function Le(t,e={}){if(!t)return null;let o=K(),s=k(),n=o[t];if(n){let r=["title","company","companyUrl","location","salary","url","applied","appliedDate","recruiterUrl","description","techStack","jobTags","seniority","employmentType","industries","jobFunction","postedTime","applicantCount","companySize","workplaceType","contractType","isEasyApply","applyType","scoutedAt","lastSeen"];for(let i of r)e[i]!=null&&(n[i]=e[i])}else o[t]={title:e.title||null,company:e.company||null,location:e.location||null,salary:e.salary||null,url:e.url||null,applied:e.applied||!1,appliedDate:e.appliedDate||s,recruiterUrl:e.recruiterUrl||null,firstSeen:s};return H(o),o[t]}function ke(t,e){if(!t||!e)return;let o=O(),s=o[t];s&&(s.jobIds||(s.jobIds=[]),s.jobIds.includes(e)||(s.jobIds.push(e),s.lastUpdated=new Date().toISOString(),A(o)))}function Ne(t,e){if(!t||!e)return;let o=O(),s=o[t];if(!s)return;s.encounters||(s.encounters=[]);let r={date:k(),source:e.source||"unknown",action:e.action||"unknown"};e.jobId&&(r.jobId=String(e.jobId)),e.jobTitle&&(r.jobTitle=e.jobTitle),e.jobCompany&&(r.jobCompany=e.jobCompany),!s.encounters.some(c=>c.date===r.date&&c.source===r.source&&c.action===r.action&&(c.jobId||"")===(r.jobId||""))&&(s.encounters.push(r),s.lastUpdated=new Date().toISOString(),A(o))}function Me(t){return t&&O()[t]?.connectionStatus||null}function Je(t,e,o={}){if(!t)return;let s=O(),n=s[t];if(n||(fe(t,o.profileInfo||{}),n=s[t]),!!n){if(n.connectionStatus=e,n.connectionDate=k(),n.connectionMethod=o.method||n.connectionMethod,n.messageSent=o.messageSent||n.messageSent,n.lastUpdated=new Date().toISOString(),o.profileInfo)for(let r of pe)o.profileInfo[r]!=null&&(n[r]=o.profileInfo[r]);A(s)}}function X(){return I||(S(),I=P(Ee,"quota.json"),(!I||!I.connections)&&(I={connections:[]}),I)}function de(t){S(),I=t;try{C(Ee,t)}catch(e){console.error(`[!] Failed to save quota.json: ${e.message}`)}}function Pe(t,e="unknown"){let o=X(),s=new Date;o.connections.push({date:s.toISOString().split("T")[0],time:s.toTimeString().split(" ")[0],profileUrl:t,source:e}),de(o)}function Z(){let t=X(),e=new Date;e.setDate(e.getDate()-7);let o=e.toISOString().split("T")[0];return t.connections.filter(s=>s.date>=o)}function ee(){let t=Z(),e=k(),o=t.filter(a=>a.date===e).length,s=t.length,n=o>=M,r=s>=J,i=!n&&!r,c={allowed:i,daily:{sent:o,max:M},weekly:{sent:s,max:J}};return i||(c.reason=n?`Daily limit reached (${o}/${M})`:`Weekly limit reached (${s}/${J})`),c}function te(){let t=Z(),e=k(),o=t.filter(n=>n.date===e).length,s=t.length;return{daily:{sent:o,max:M,remaining:M-o},weekly:{sent:s,max:J,remaining:J-s}}}function Et(t){if(!t)return null;let e=t.match(/jobs\/view\/(\d+)/);return e?e[1]:null}function Ue(){S();let t=d.readdirSync(h).filter(r=>r.endsWith(".json")&&r!=="people.json"&&r!=="jobs.json"&&r!=="quota.json");if(t.length===0)return console.log("No daily files found to migrate."),{people:0,jobs:0};console.log(`
|
|
2
2
|
Migrating from ${t.length} daily files...
|
|
3
|
-
`);let e=
|
|
4
|
-
Migration complete!`),console.log(` People: ${Object.keys(e).length} total (${s} new)`),console.log(` Jobs: ${Object.keys(o).length} total (${n} new)`),{people:Object.keys(e).length,jobs:Object.keys(o).length}}function qe(){return L||(S(),L=P(_e,"sync-state.json")||{},L)}function Ve(t){S(),L=t;try{C(_e,t)}catch(e){console.error(`[!] Failed to save sync-state.json: ${e.message}`)}}function me(){if(R)return R;S();try{d.existsSync(ue)&&(R=JSON.parse(d.readFileSync(ue,"utf-8")))}catch{}return R||(R={}),R}function Be(t){return Array.isArray(t.applications)}function Qe(){let t=me();if(Be(t))return t.applications;let e=[];for(let o of Object.values(t))o.applications&&e.push(...o.applications);return e}function Ye(t){if(!t||!t.questions||t.questions.length===0)return;let e=me(),o=t.jobId||"unknown",n={applicationId:`${o}_${Date.now()}`,date:new Date().toISOString(),result:t.result||"submitted",formPages:t.formPages||null,duration:t.duration||null,retries:t.retries||0,failedField:t.failedField||null,questionCount:t.questions.length,questions:t.questions.map(r=>{let i={type:r.type||"input",question:r.labelText||r.question||"",answer:r.answer||""};return r.options&&(r.options.length<=_t?i.options=r.options:i.optionsOmitted=!0),r.page&&(i.page=r.page),r.wasPreFilled&&(i.wasPreFilled=!0),r.wasRetry&&(i.wasRetry=!0),i})};Be(e)?(n.jobId=o,n.jobTitle=t.jobTitle||null,n.jobCompany=t.jobCompany||null,n.jobUrl=t.jobUrl||null,e.applications.push(n)):(e[o]||(e[o]={applications:[]}),e[o].applications.push(n));try{C(ue,e)}catch(r){console.error(`[!] Failed to save application-questions.json: ${r.message}`)}}function ze(){let t=Qe(),e=new Map;for(let r of t)for(let i of r.questions||[]){let c=i.question.toLowerCase().trim();if(!c)continue;e.has(c)||e.set(c,{question:i.question,count:0,answers:new Map});let a=e.get(c);a.count++;let f=(i.answer||"").trim();a.answers.set(f,(a.answers.get(f)||0)+1)}let o=[...e.values()].sort((r,i)=>i.count-r.count),s=o.slice(0,15).map(r=>({question:r.question,count:r.count,topAnswer:[...r.answers.entries()].sort((i,c)=>c[1]-i[1])[0]?.[0]||""})),n=o.filter(r=>r.answers.size>1&&r.count>=3).map(r=>({question:r.question,count:r.count,answers:[...r.answers.entries()].sort((i,c)=>c[1]-i[1]).map(([i,c])=>`${i} (${c}x)`)}));return{totalApplications:t.length,uniqueQuestions:e.size,topQuestions:s,inconsistent:n}}function We(t){if(d.existsSync(m.join(h,".git")))try{let e={cwd:h,stdio:"pipe",timeout:1e4};ce("git add conversations.json people.json jobs.json quota.json sync-state.json run-log.json application-questions.json",e);try{ce("git diff --cached --quiet",e);return}catch{}let o=(t||`sync ${new Date().toISOString().slice(0,16)}`).replace(/"/g,'\\"');ce(`git commit -m "${o}"`,e)}catch(e){process.env.VERBOSE&&console.log(` [git] snapshot skipped: ${e.message?.slice(0,80)}`)}}var Rt,Tt,h,le,ae,Ee,G,_e,ue,M,J,x,F,I,T,L,R,pe,_t,xt,ge=De(()=>{Rt
|
|
3
|
+
`);let e=O(),o=K(),s=0,n=0;t.sort();for(let r of t){let i=m.join(h,r),c;try{c=JSON.parse(d.readFileSync(i,"utf-8"))}catch(u){console.log(` Skipping ${r}: ${u.message}`);continue}let a=c.date||r.match(/\d{4}-\d{2}-\d{2}/)?.[0]||null,f=c.source||r.split("-").slice(0,-1).join("-").replace(/-\d{4}$/,"")||"unknown";if(!c.profiles||!Array.isArray(c.profiles)){console.log(` Skipping ${r}: no profiles array`);continue}console.log(` ${r}: ${c.profiles.length} profiles`);for(let u of c.profiles){let v=u.profileUrl;if(!v)continue;let l=e[v];if(l){l.seenCount=(l.seenCount||1)+1,a&&(!l.lastSeen||a>l.lastSeen)&&(l.lastSeen=a),a&&(!l.firstSeen||a<l.firstSeen)&&(l.firstSeen=a),f&&!l.sources?.includes(f)&&(l.sources=l.sources||[],l.sources.push(f));for(let p of pe)u[p]!=null&&(l[p]=u[p]);u.status&&u.processed&&(l.connectionStatus=u.status,l.connectionDate=a,u.funnelResult&&(l.connectionMethod=u.funnelResult.method||l.connectionMethod,l.messageSent=u.funnelResult.message||l.messageSent)),l.lastUpdated=new Date().toISOString()}else e[v]={name:u.name||null,title:u.title||null,location:u.location||null,company:u.company||null,connectionDegree:u.connectionDegree||null,connectionsCount:u.connectionsCount||null,mutualConnections:u.mutualConnections||null,photoUrl:u.photoUrl||null,hasVerifiedBadge:u.hasVerifiedBadge||!1,firstSeen:a,lastSeen:a,seenCount:1,sources:f?[f]:[],connectionStatus:u.processed&&u.status||null,connectionDate:u.processed?a:null,connectionMethod:u.funnelResult?.method||null,messageSent:u.funnelResult?.message||null,jobIds:[],lastUpdated:new Date().toISOString()},s++;if(u.fromJob?.url){let p=Et(u.fromJob.url);p&&(e[v].jobIds||(e[v].jobIds=[]),e[v].jobIds.includes(p)||e[v].jobIds.push(p),o[p]?o[p].recruiterUrl||(o[p].recruiterUrl=v):(o[p]={title:u.fromJob.title||null,company:u.fromJob.company||null,location:null,salary:null,url:u.fromJob.url,applied:!0,appliedDate:a,recruiterUrl:v,firstSeen:a},n++))}}}return A(e),H(o),console.log(`
|
|
4
|
+
Migration complete!`),console.log(` People: ${Object.keys(e).length} total (${s} new)`),console.log(` Jobs: ${Object.keys(o).length} total (${n} new)`),{people:Object.keys(e).length,jobs:Object.keys(o).length}}function qe(){return L||(S(),L=P(_e,"sync-state.json")||{},L)}function Ve(t){S(),L=t;try{C(_e,t)}catch(e){console.error(`[!] Failed to save sync-state.json: ${e.message}`)}}function me(){if(R)return R;S();try{d.existsSync(ue)&&(R=JSON.parse(d.readFileSync(ue,"utf-8")))}catch{}return R||(R={}),R}function Be(t){return Array.isArray(t.applications)}function Qe(){let t=me();if(Be(t))return t.applications;let e=[];for(let o of Object.values(t))o.applications&&e.push(...o.applications);return e}function Ye(t){if(!t||!t.questions||t.questions.length===0)return;let e=me(),o=t.jobId||"unknown",n={applicationId:`${o}_${Date.now()}`,date:new Date().toISOString(),result:t.result||"submitted",formPages:t.formPages||null,duration:t.duration||null,retries:t.retries||0,failedField:t.failedField||null,questionCount:t.questions.length,questions:t.questions.map(r=>{let i={type:r.type||"input",question:r.labelText||r.question||"",answer:r.answer||""};return r.options&&(r.options.length<=_t?i.options=r.options:i.optionsOmitted=!0),r.page&&(i.page=r.page),r.wasPreFilled&&(i.wasPreFilled=!0),r.wasRetry&&(i.wasRetry=!0),typeof r.isRequired=="boolean"&&(i.isRequired=r.isRequired),r.wasOptional&&(i.wasOptional=!0),i})};Be(e)?(n.jobId=o,n.jobTitle=t.jobTitle||null,n.jobCompany=t.jobCompany||null,n.jobUrl=t.jobUrl||null,e.applications.push(n)):(e[o]||(e[o]={applications:[]}),e[o].applications.push(n));try{C(ue,e)}catch(r){console.error(`[!] Failed to save application-questions.json: ${r.message}`)}}function ze(){let t=Qe(),e=new Map;for(let r of t)for(let i of r.questions||[]){let c=i.question.toLowerCase().trim();if(!c)continue;e.has(c)||e.set(c,{question:i.question,count:0,answers:new Map});let a=e.get(c);a.count++;let f=(i.answer||"").trim();a.answers.set(f,(a.answers.get(f)||0)+1)}let o=[...e.values()].sort((r,i)=>i.count-r.count),s=o.slice(0,15).map(r=>({question:r.question,count:r.count,topAnswer:[...r.answers.entries()].sort((i,c)=>c[1]-i[1])[0]?.[0]||""})),n=o.filter(r=>r.answers.size>1&&r.count>=3).map(r=>({question:r.question,count:r.count,answers:[...r.answers.entries()].sort((i,c)=>c[1]-i[1]).map(([i,c])=>`${i} (${c}x)`)}));return{totalApplications:t.length,uniqueQuestions:e.size,topQuestions:s,inconsistent:n}}function We(t){if(d.existsSync(m.join(h,".git")))try{let e={cwd:h,stdio:"pipe",timeout:1e4};ce("git add conversations.json people.json jobs.json quota.json sync-state.json run-log.json application-questions.json",e);try{ce("git diff --cached --quiet",e);return}catch{}let o=(t||`sync ${new Date().toISOString().slice(0,16)}`).replace(/"/g,'\\"');ce(`git commit -m "${o}"`,e)}catch(e){process.env.VERBOSE&&console.log(` [git] snapshot skipped: ${e.message?.slice(0,80)}`)}}var Rt,Tt,h,le,ae,Ee,G,_e,ue,M,J,x,F,I,T,L,R,pe,_t,xt,ge=De(()=>{Rt=$t(import.meta.url),Tt=m.dirname(m.dirname(Rt)),h=process.env.DATA_DIR?m.resolve(process.env.DATA_DIR,"collected-profiles"):m.join(Tt,"collected-profiles"),le=m.join(h,"people.json"),ae=m.join(h,"jobs.json"),Ee=m.join(h,"quota.json"),G=m.join(h,"conversations.json"),_e=m.join(h,"sync-state.json"),ue=m.join(h,"application-questions.json"),M=20,J=100,x=null,F=null,I=null,T=null,L=null,R=null;pe=["name","title","location","company","connectionDegree","connectionsCount","mutualConnections","photoUrl","hasVerifiedBadge"];_t=50;xt={loadPeople:O,savePeople:A,loadJobs:K,saveJobs:H,loadConversations:xe,saveConversations:Fe,upsertPerson:fe,upsertJob:Le,linkPersonToJob:ke,addEncounter:Ne,getPersonStatus:Me,updateConnectionStatus:Je,migrateFromDailyFiles:Ue,loadQuota:X,saveQuota:de,recordConnectionSent:Pe,canSendConnection:ee,getRecentConnections:Z,getQuotaStatus:te,loadSyncState:qe,saveSyncState:Ve,commitDataSnapshot:We,logApplicationQuestions:Ye,getQuestionStats:ze}});var ot={};Ie(ot,{getJobDecision:()=>Pt,getLastVisit:()=>Bt,getScoutStats:()=>Qt,loadCompanies:()=>tt,loadDiscoveredPeople:()=>ye,loadJobDecisions:()=>Se,loadScoutedJobs:()=>he,loadVisitLog:()=>oe,recordVisit:()=>Vt,resetCaches:()=>Nt,saveCompany:()=>qt,saveDiscoveredPerson:()=>Ut,saveScoutedJob:()=>Mt,setJobDecision:()=>Jt});import"dotenv/config";import*as D from"fs";import*as y from"path";import{fileURLToPath as Ft}from"url";function Q(){D.existsSync(_)||D.mkdirSync(_,{recursive:!0})}function Y(t,e){let o=JSON.stringify(e,null,2),s=t+".tmp";D.writeFileSync(s,o);for(let n=0;n<3;n++)try{D.renameSync(s,t);return}catch(r){if(n<2&&(r.code==="EPERM"||r.code==="EBUSY")){let i=Date.now();for(;Date.now()-i<200;);continue}throw r}}function z(t){try{if(D.existsSync(t))return JSON.parse(D.readFileSync(t,"utf-8"))}catch{}return null}function Nt(){U=null,q=null,V=null,B=null,E=null}function he(){return U||(U=z(Ke)||{},U)}function Mt(t,e){if(!t)return;Q();let o=he(),s=new Date().toISOString().slice(0,10);if(o[t]){for(let[n,r]of Object.entries(e))r!=null&&(o[t][n]=r);o[t].lastSeen=s}else o[t]={...e,scoutedAt:s,lastSeen:s};try{Y(Ke,o)}catch(n){console.error(`[!] Failed to save scouted-jobs.json: ${n.message}`)}}function Se(){return q||(q=z(et)||{},q)}function Jt(t,e,o={}){if(!t||!e)return;Q();let s=Se(),n=new Date().toISOString().slice(0,10);s[t]?(s[t].status=e,s[t].updatedAt=n,o.reason&&(s[t].reason=o.reason),o.score!=null&&(s[t].score=o.score),o.source&&(s[t].source=o.source)):s[t]={status:e,decidedAt:n,updatedAt:n,reason:o.reason||null,score:o.score??null,source:o.source||null};try{Y(et,s)}catch(r){console.error(`[!] Failed to save job-decisions.json: ${r.message}`)}}function Pt(t){return Se()[t]||null}function ye(){return V||(V=z(He)||{},V)}function Ut(t,e){if(!t)return;Q();let o=ye(),s=new Date().toISOString().slice(0,10);if(o[t]){let n=o[t];e.name&&(n.name=e.name),e.title&&(n.title=e.title),e.company&&(n.company=e.company),e.location&&(n.location=e.location),e.role&&(n.role=e.role),e.isConnected!=null&&(n.isConnected=e.isConnected),e.jobId&&(n.jobIds||(n.jobIds=[]),n.jobIds.includes(e.jobId)||n.jobIds.push(e.jobId)),n.lastSeen=s,n.seenCount=(n.seenCount||1)+1}else o[t]={name:e.name||null,title:e.title||null,company:e.company||null,location:e.location||null,role:e.role||null,source:e.source||"scout",isConnected:e.isConnected||!1,discoveredAt:s,lastSeen:s,seenCount:1,jobIds:e.jobId?[e.jobId]:[]};try{Y(He,o)}catch(n){console.error(`[!] Failed to save discovered-people.json: ${n.message}`)}}function tt(){return B||(B=z(Ze)||{},B)}function qt(t,e){if(!t)return;Q();let o=tt(),s=new Date().toISOString().slice(0,10);if(o[t]){let n=o[t];e.name&&(n.name=e.name),e.about&&(n.about=e.about),e.size&&(n.size=e.size),e.industries&&(n.industries=e.industries),e.jobId&&(n.jobIds||(n.jobIds=[]),n.jobIds.includes(e.jobId)||n.jobIds.push(e.jobId)),e.recruiterUrl&&(n.recruiterUrls||(n.recruiterUrls=[]),n.recruiterUrls.includes(e.recruiterUrl)||n.recruiterUrls.push(e.recruiterUrl)),n.lastSeen=s,n.jobCount=n.jobIds?.length||0}else o[t]={name:e.name||null,about:e.about||null,size:e.size||null,industries:e.industries||null,discoveredAt:s,lastSeen:s,jobIds:e.jobId?[e.jobId]:[],recruiterUrls:e.recruiterUrl?[e.recruiterUrl]:[],jobCount:e.jobId?1:0};try{Y(Ze,o)}catch(n){console.error(`[!] Failed to save companies.json: ${n.message}`)}}function oe(){return E||(E=z(Xe)||{visits:[]},Array.isArray(E.visits)||(E.visits=[]),E)}function Vt(t,e={}){if(!t)return;Q();let o=oe();o.visits.push({date:new Date().toISOString(),profileUrl:t,name:e.name||null,source:e.source||"manual"});let s=Date.now()-90*24*60*60*1e3;o.visits=o.visits.filter(n=>new Date(n.date).getTime()>s);try{Y(Xe,o)}catch(n){console.error(`[!] Failed to save visit-log.json: ${n.message}`)}}function Bt(t){let e=oe();for(let o=e.visits.length-1;o>=0;o--)if(e.visits[o].profileUrl===t)return e.visits[o].date;return null}function Qt(){let t=he(),e=ye(),o=Object.values(t),s=Object.values(e),n={};for(let l of o)l.company&&(n[l.company]=(n[l.company]||0)+1);let r=Object.entries(n).sort((l,p)=>p[1]-l[1]).slice(0,5).map(([l,p])=>`${l} (${p})`),i={};for(let l of o)for(let p of l.techStack||[])i[p]=(i[p]||0)+1;let c=Object.entries(i).sort((l,p)=>p[1]-l[1]).slice(0,8).map(([l,p])=>`${l} (${p})`),a=oe(),f=new Date().toISOString().slice(0,10),u=a.visits.filter(l=>l.date.startsWith(f)).length,v=new Set(a.visits.map(l=>l.profileUrl)).size;return{totalJobs:o.length,totalRecruiters:s.length,topCompanies:r,topTech:c,totalVisited:v,todayVisited:u,lastScoutDate:o.length>0?o.sort((l,p)=>(p.scoutedAt||"").localeCompare(l.scoutedAt||""))[0]?.scoutedAt:null}}var Lt,kt,_,Ke,He,Xe,Ze,et,U,q,V,B,E,nt=De(()=>{Lt=Ft(import.meta.url),kt=y.dirname(y.dirname(Lt)),_=process.env.DATA_DIR?y.resolve(process.env.DATA_DIR,"market-research"):y.join(kt,"market-research"),Ke=y.join(_,"scouted-jobs.json"),He=y.join(_,"discovered-people.json"),Xe=y.join(_,"visit-log.json"),Ze=y.join(_,"companies.json"),et=y.join(_,"job-decisions.json");U=null;q=null;V=null;B=null;E=null});import{execFileSync as Ht}from"child_process";import"dotenv/config";import*as b from"fs";import*as w from"path";import{fileURLToPath as Dt}from"url";import"dotenv/config";import{createGraphClient as St,resolveToken as yt}from"@ilivemylife/graph-sdk";import{config as jt}from"dotenv";jt();var we=yt()||process.env.TOKEN;we||(console.error(`
|
|
5
5
|
[FATAL] No iLiveMyLife token found.`),console.error(" The bot uses Lifebot AI to decide which jobs to apply for"),console.error(` and how to fill application forms. It cannot work without it.
|
|
6
6
|
`),console.error(" Easiest way:"),console.error(" 1. npm install -g @ilivemylife/graph-sdk"),console.error(` 2. ilml login your@email.com yourpassword
|
|
7
7
|
`),console.error(` Or add ILML_TOKEN=... to .env
|
|
8
|
-
`),process.exit(1));var
|
|
8
|
+
`),process.exit(1));var Oe=St({token:we});function vt(t){if(!t||t<1e3)return`${t||0}ms`;let e=Math.round(t/1e3);if(e<60)return`${e}s`;let o=Math.floor(e/60),s=e%60;return s?`${o}m ${s}s`:`${o}m`}function bt({scriptName:t,mode:e,status:o,error:s,duration:n}){let r=o==="error"?"\u2717":o==="quota-reached"?"\u26A0":"\u2713",i=new Date().toISOString().slice(0,16).replace("T"," "),c=n?` ${vt(n)}`:"",a=o==="error"?`error${s?`: ${String(s).slice(0,80)}`:""}`:o==="quota-reached"?"daily quota reached":"done",f=e&&e!=="default"?` (${e})`:"";return`${r} ${t}${f} \u2014 ${a} \u2014 ${i}${c}`}async function $e({scriptName:t,mode:e,status:o,summaryLines:s,error:n,duration:r}){let i=process.env.NODE_RUN_REPORTS;if(!i||!Array.isArray(s)||s.length===0)return;let a=`**${bt({scriptName:t,mode:e,status:o,error:n,duration:r})}**
|
|
9
9
|
|
|
10
10
|
${s.join(`
|
|
11
|
-
`)}`;try{await
|
|
11
|
+
`)}`;try{await Oe.addMessage(i,a)}catch(f){console.error(`[runReport] Failed to post end-of-session message: ${f.message}`)}}var It=Dt(import.meta.url),wt=w.dirname(w.dirname(It)),re=process.env.DATA_DIR?w.resolve(process.env.DATA_DIR,"collected-profiles"):w.join(wt,"collected-profiles"),ie=w.join(re,"run-log.json");function Ot(t,e){let o=JSON.stringify(e,null,2),s=t+".tmp";b.writeFileSync(s,o);for(let n=0;n<3;n++)try{b.renameSync(s,t);return}catch(r){if(n<2&&(r.code==="EPERM"||r.code==="EBUSY")){let i=Date.now();for(;Date.now()-i<200;);continue}throw r}}function Re(){try{if(!b.existsSync(ie))return{runs:[]};let t=JSON.parse(b.readFileSync(ie,"utf-8"));return!t.runs||!Array.isArray(t.runs)?{runs:[]}:t}catch{return{runs:[]}}}function Te(t){b.existsSync(re)||b.mkdirSync(re,{recursive:!0});try{Ot(ie,t)}catch(e){console.error(`[!] Failed to save run-log.json: ${e.message}`)}}function Ce(t,e="default"){let o=Re();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 s=new Date,n={timestamp:s.toISOString(),script:t,mode:e,status:"running",result:null,duration:null,stats:null,error:null};o.runs.push(n);let r=Date.now()-30*24*60*60*1e3;return o.runs=o.runs.filter(i=>new Date(i.timestamp).getTime()>r),Te(o),{script:t,mode:e,startTime:s.getTime(),runIndex:o.runs.length-1}}function Ae(t,e,o=null,s=null){if(!t)return;let n=null;try{let i=Re(),c=i.runs[t.runIndex];if(c&&c.status==="running"&&c.script===t.script)c.status=e,c.result=e,c.duration=Date.now()-t.startTime,c.stats=o,c.error=s,n=c.duration;else{let a=i.runs.find(f=>f.script===t.script&&f.status==="running"&&f.timestamp===new Date(t.startTime).toISOString());a&&(a.status=e,a.result=e,a.duration=Date.now()-t.startTime,a.stats=o,a.error=s,n=a.duration)}Te(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=e==="error"?"error":o&&o.dailyLimitHit?"quota-reached":"done";$e({scriptName:t.script,mode:t.mode,status:i,summaryLines:r,error:s,duration:n??Date.now()-t.startTime}).catch(c=>console.error(`[runLog] postRunReport failed: ${c.message}`))}}ge();import"dotenv/config";import*as j from"fs";import*as g from"path";import{fileURLToPath as Yt}from"url";var zt=Yt(import.meta.url),Wt=g.dirname(zt),it=g.dirname(g.dirname(Wt)),ne=process.env.DATA_DIR?g.resolve(process.env.DATA_DIR,"collected-profiles"):g.join(it,"collected-profiles"),se=process.env.DATA_DIR?g.resolve(process.env.DATA_DIR,"market-research"):g.join(it,"market-research"),je=1,Gt=[{version:1,file:"./v001-normalize-jobs-and-questions.mjs"}];function ct(t){return g.join(t,".schema-version")}function ve(t){let e=ct(t);if(!j.existsSync(e))return{version:0,migrations:[]};try{return JSON.parse(j.readFileSync(e,"utf-8"))}catch{return{version:0,migrations:[]}}}function Kt(t,e){j.writeFileSync(ct(t),JSON.stringify(e,null,2))}function st(t,e){if(!j.existsSync(t))return;let o=`.pre-v${String(e).padStart(3,"0")}.backup`,s=j.readdirSync(t).filter(n=>n.endsWith(".json")&&!n.includes(".backup"));for(let n of s){let r=g.join(t,n),i=g.join(t,n+o);try{j.copyFileSync(r,i)}catch(c){console.error(` [!] Failed to backup ${n}: ${c.message}`)}}s.length>0&&console.log(` Backed up ${s.length} files in ${g.basename(t)}/ (${o})`)}var rt=!1;async function lt(){if(rt)return;rt=!0;let t=ve(ne),e=ve(se),o=Math.min(t.version,e.version);if(o>=je)return;console.log(`
|
|
12
12
|
\u{1F4E6} Schema migration: v${o} \u2192 v${je}`);let s=Gt.filter(n=>n.version>o);for(let n of s){console.log(`
|
|
13
13
|
Running migration v${String(n.version).padStart(3,"0")}...`),st(ne,n.version),st(se,n.version);let r=await import(n.file);try{await r.up(ne,se)}catch(a){console.error(`
|
|
14
14
|
[FATAL] Migration v${n.version} failed: ${a.message}`),console.error(` Backups are in collected-profiles/ and market-research/ (.pre-v${String(n.version).padStart(3,"0")}.backup)`),console.error(` Fix the issue and restart.
|
|
15
15
|
`),process.exit(1)}let i=new Date().toISOString(),c={version:n.version,name:r.NAME||`v${String(n.version).padStart(3,"0")}`,appliedAt:i};for(let a of[ne,se]){if(!j.existsSync(a))continue;let f=ve(a);f.version=n.version,f.migratedAt=i,f.migrations.push(c),Kt(a,f)}console.log(` \u2713 Migration v${String(n.version).padStart(3,"0")} complete`)}try{let{resetCaches:n}=await Promise.resolve().then(()=>(ge(),Ge));n()}catch{}try{let{resetCaches:n}=await Promise.resolve().then(()=>(nt(),ot));n()}catch{}console.log(`
|
|
16
16
|
\u2713 Schema is now at v${je}
|
|
17
|
-
`)}await lt();var W=process.argv.slice(2),be=new Set((W.find(t=>t.startsWith("--skip="))?.split("=")[1]||"").split(",").filter(Boolean)),at=new Set((W.find(t=>t.startsWith("--only="))?.split("=")[1]||"").split(",").filter(Boolean)),ft=W.includes("--dry-run"),dt=W.includes("--jobs"),mt=W.includes("--comms"),ut=["apply-queue","apply","funnel","scout","visit"],pt=["sync","today"],gt=dt?ut:mt?pt:[...ut,...pt];function
|
|
17
|
+
`)}await lt();var W=process.argv.slice(2),be=new Set((W.find(t=>t.startsWith("--skip="))?.split("=")[1]||"").split(",").filter(Boolean)),at=new Set((W.find(t=>t.startsWith("--only="))?.split("=")[1]||"").split(",").filter(Boolean)),ft=W.includes("--dry-run"),dt=W.includes("--jobs"),mt=W.includes("--comms"),ut=["apply-queue","apply","funnel","scout","visit"],pt=["sync","today"],gt=dt?ut:mt?pt:[...ut,...pt];function $(t){return at.size>0?at.has(t):!be.has(t)&>.includes(t)}function N(t,e,o=[],{optional:s=!1}={}){if(console.log(`
|
|
18
18
|
${"\u2550".repeat(50)}`),console.log(` STEP: ${t}`),console.log("\u2550".repeat(50)),ft)return console.log(` [DRY RUN] Would run: node ${e} ${o.join(" ")}`),!0;try{return Ht("node",[e,...o],{stdio:"inherit",timeout:18e6}),console.log(` \u2713 ${t} completed`),!0}catch(n){return s?(console.log(` \u26A0 ${t} failed (optional): ${n.message?.slice(0,80)}`),!1):(console.error(` \u2717 ${t} failed: ${n.message?.slice(0,80)}`),!1)}}async function Xt(){let t=dt?"jobs":mt?"comms":"full";console.log(`
|
|
19
|
-
\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\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(` DAILY PIPELINE (${t})`),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");let e=gt.filter(
|
|
20
|
-
\u26A0 Funnel skipped: ${r.reason}`),s.funnel="skipped_quota")}
|
|
19
|
+
\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\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(` DAILY PIPELINE (${t})`),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");let e=gt.filter($);console.log(` Steps: ${e.join(" \u2192 ")}`),be.size>0&&console.log(` Skipping: ${[...be].join(", ")}`),ft&&console.log(" [DRY RUN MODE]");let o=Ce("daily",t),s={};if($("apply-queue")&&(s["apply-queue"]=N("Apply Queue (scored jobs)","applyQueue.mjs",["--max=50","--min-score=50"],{optional:!0})),$("apply")){let r=te();s.apply=N("Apply from Search","run.mjs",["--no-wait"],{optional:!0})}if($("funnel")&&!$("apply")){let r=ee();r.allowed?s.funnel=N("Connection Funnel","funnelRunner.mjs",["--max=20"],{optional:!0}):(console.log(`
|
|
20
|
+
\u26A0 Funnel skipped: ${r.reason}`),s.funnel="skipped_quota")}$("scout")&&(s.scout=N("Scout Jobs for Tomorrow","scout.mjs",["--max=300","--verbose"],{optional:!0})),$("sync")&&(s.sync=N("Sync Inbox","syncAll.mjs")),$("today")&&(s.today=N("Daily Report","syncAll.mjs",["--today"])),console.log(`
|
|
21
21
|
\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\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(" PIPELINE COMPLETE"),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");for(let[r,i]of Object.entries(s))console.log(` ${i===!0?"\u2713":i===!1?"\u2717":"\u26A0"} ${r}: ${i===!0?"success":i===!1?"failed":i}`);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
|
|
22
22
|
`);let n=Object.values(s).some(r=>r===!1);Ae(o,n?"partial":"success",s)}Xt().catch(t=>{console.error("Daily pipeline failed:",t),process.exit(1)});
|