Commit Graph

34 Commits

Author SHA1 Message Date
69eb6b124f Classify unknown_external jobs by following Apply redirects
After LinkedIn search completes, visits each unknown_external job page,
clicks the Apply button, captures the redirect URL, and matches against
known ATS patterns to identify the actual application platform.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 18:13:32 -08:00
6d325cf931 Add timestamps to searcher start/finish logs for run tracking
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 14:22:48 -08:00
d5e595475c fix: 6h cooldown guard on searcher — prevents accidental re-runs 2026-03-06 22:18:25 +00:00
c99ea10585 Richer search and filter summaries
Search: show per-track breakdown (found/added per track name)
Filter: show top 5 scoring jobs with score, title, company and cost

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 13:34:21 -08:00
4419363b3c Fix process exit: use process.exit() directly instead of logStream.end callback
logStream.end() callback wasn't firing reliably, leaving processes hanging.
process.exit() is synchronous and forces exit regardless of open handles.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 12:21:55 -08:00
d43e2025b2 Fix process not exiting after run, detect closed job listings
- All entry points with log tee now call logStream.end() + process.exit()
  (log stream kept event loop alive, blocking next cron run)
- easy_apply: detect "no longer accepting applications" and similar closed
  listing text before reporting as unsupported

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 12:19:00 -08:00
51ca354c52 Audit fixes: remove dead code, fix run timeout bug, add log tee to all entry points
- Remove unused APPLY_PRIORITY array (replaced by score-based sort)
- Fix run timeout only breaking inner loop — now breaks outer platform loop too
- Remove dead lastProgress variable in easy_apply step loop
- Add stdout/stderr log tee to job_searcher, job_filter, telegram_poller

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 12:13:01 -08:00
b1528ac0ad refactor: extract magic numbers to constants, fix audit issues
- Centralize all magic numbers/strings in lib/constants.mjs
- Fix double-replaced import names in filter.mjs
- Consolidate duplicate fs imports in job_applier/job_searcher
- Remove empty JSDoc block in job_searcher
- Update keywords.mjs model from claude-3-haiku to claude-haiku-4-5
- Extract Anthropic API URLs to constants
- Convert :has-text() selectors to page.locator() API
- Fix SIGTERM handler conflict — move partial-run notification into lock.onShutdown
- Remove unused exports (LOCAL_USER_AGENT, DEFAULT_REVIEW_WINDOW_MINUTES)
- Fix variable shadowing (b -> v) in job_filter reduce callback
- Replace SKILL.md PM2 references with system cron

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 08:45:17 -08:00
3a52bdc72e feat: dynamic lookback window — time since last run × 1.25 buffer (min 4h) 2026-03-06 16:25:09 +00:00
8748c0e383 fix: send telegram on SIGTERM so partial cron runs still notify when jobs found 2026-03-06 11:16:39 +00:00
d4cc5b2541 fix: only send telegram when new jobs found (>0) 2026-03-06 11:14:50 +00:00
00b0094d97 feat: always send telegram after searcher run, not just when new jobs found 2026-03-06 11:14:30 +00:00
d610060dbb feat: persistent run history logs for searcher and filter
- search_runs.json: append-only history of every searcher run
  (started_at, finished, added, seen, platforms, lookback_days)
- search_progress_last.json: snapshot of final progress state after
  each completed run — answers 'what keywords/tracks were searched?'
- filter_runs.json: append-only history of every filter batch
  (batch_id, submitted/collected timestamps, model, passed/filtered/errors)
Fixes the 'did the 90-day run complete?' ambiguity going forward
2026-03-06 10:16:06 +00:00
4a2b24d562 fix: show global keyword position (2/20) not slice position (1/19) in logs 2026-03-06 02:47:39 +00:00
2a4fd52d43 fix: clear progress on clean finish — keywords only reused when previous run was incomplete 2026-03-06 02:26:14 +00:00
97b753d401 feat: cache keywords in search_progress.json — restarts reuse same keywords, no regeneration mid-run 2026-03-06 02:24:54 +00:00
65d6d1e50c feat: per-keyword resume — restart picks up from last completed keyword, not keyword 1 2026-03-06 02:23:07 +00:00
261f5800ad fix: load API keys from .env at runtime — never embed credentials in cron payloads or source code 2026-03-06 02:04:55 +00:00
ed908c91af fix: audit bugs + better error logging for searcher debugging
Bugs fixed:
- form_filler.mjs: add missing await on el.evaluate() in getLabel()
- analyze_ats.mjs: check job.apply_type instead of non-existent job.easy_apply
- status.mjs: fix typo "that run" → "in that run"
- README: add missing lock.mjs to project structure

Logging improvements:
- job_searcher.mjs: log browser creation, login steps, stack traces on error
- linkedin.mjs/wellfound.mjs: catch and log navigation failures per keyword
- browser.mjs: descriptive errors for Kernel and CDP connection failures

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 17:28:23 -08:00
fb7fc31fe1 fix: move Kernel connection IDs out of source into settings.json (gitignored) 2026-03-06 01:17:52 +00:00
f065a9a786 feat: auto-refresh Kernel Managed Auth session on login failure — ensureLoggedIn() with retry 2026-03-06 01:09:30 +00:00
2574276a85 refactor: classify apply_type inline during search (click card → detect right panel); no second pass 2026-03-06 01:00:59 +00:00
dee6e98603 feat: searcher Phase 2 classifies apply type; applier sorts by priority; already-applied detection 2026-03-06 00:58:22 +00:00
f9446f5bee fix: resume lookback from progress file — don't reset to 2d when queue is non-empty 2026-03-06 00:41:27 +00:00
86d00297df feat: search progress tracking — resume on restart, skip completed platform:track combinations 2026-03-06 00:37:35 +00:00
b091473735 fix: graceful shutdown — write last-run file on SIGTERM, show interrupted state in status 2026-03-06 00:35:10 +00:00
1920df51a4 feat: rich status report — searcher/applier last run time, timeAgo, per-run metadata files 2026-03-06 00:33:03 +00:00
f9fa36b47c feat: flush jobs to queue per-page — no data loss on crash; live progress output 2026-03-06 00:17:33 +00:00
234820ad91 feat: lockfile to prevent parallel runs + AI keywords lib 2026-03-06 00:11:37 +00:00
d61ca4f54f feat: AI-generated search keywords via Claude — full profile + search config context 2026-03-06 00:04:20 +00:00
e71f940687 fix: add config validation and retry logic for failed jobs
- Add loadConfig() helper with clear errors for missing/malformed JSON
- Replace raw JSON.parse(readFileSync(...)) in both entry points
- Track retry_count on jobs; re-queue as 'new' up to max_retries (default 2)
- Add max_retries and DEFAULT_MAX_RETRIES constant

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 16:03:33 -08:00
47513e8cec fix: security/bug fixes, extract constants, remove magic values
- Remove random suffix from Wellfound job IDs (broke dedup)
- Add null coalescing to all profile field returns in form_filler
- Fix honeypot case referencing nonexistent results.skipped counter
- Remove unused makeJobId import from linkedin.mjs
- Navigate directly to job URL instead of search+click in linkedin apply
- Add Telegram notification rate limiting (1.5s between sends)
- Replace Mode B blocking sleep with --preview flag
- Add max_applications_per_run enforcement
- Remove tracked search_config.json (keep .example.json only)
- Add search_config.json to .gitignore, fix duplicate node_modules entry
- Extract all magic numbers/strings to lib/constants.mjs

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 16:01:42 -08:00
cb7a401aff feat: first_run_days config — defaults to 90 days on first run, then posted_within_days after 2026-03-05 23:38:21 +00:00
52a56f59f6 feat: claw-apply v0.1 — full implementation
- job_searcher.mjs: LinkedIn + Wellfound search, queue population
- job_applier.mjs: Easy Apply + Wellfound apply, Mode A/B
- lib/form_filler.mjs: config-driven form filling, custom answers.json
- lib/linkedin.mjs: two-panel Easy Apply flow
- lib/wellfound.mjs: Wellfound search + apply
- lib/browser.mjs: Kernel stealth browser factory with local fallback
- lib/queue.mjs: jobs_queue.json management
- lib/notify.mjs: Telegram notifications
- setup.mjs: setup wizard with login verification
- Config templates: profile, search_config, answers, settings
- SKILL.md: OpenClaw skill definition
2026-03-05 23:24:09 +00:00