Commit Graph

21 Commits

Author SHA1 Message Date
1e67c930db fix: search frames for Continue /apply/ link when page.$() misses it
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 10:59:41 -08:00
46c84bf28a fix: click parent <a> link for Continue, not child <span>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 10:57:35 -08:00
4c85a88902 fix: stuck loop on unfilled selects, Continue button detection
- Stuck detection after clicking Next: if heading+progress unchanged 2x,
  exit with 'stuck' status instead of looping forever
- Select dropdowns: treat "Select an option" as unfilled (LinkedIn's
  placeholder has a truthy value that was skipping the fill logic)
- Continue button fallback: detect draft "Continue" span via apply URL
  pattern when Easy Apply button not found

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 10:51:26 -08:00
5e4a0c6599 fix: always discard on modal dismiss, support "Continue applying" button
- dismissModal now always waits for Discard confirmation after closing
  (previously returned early after Dismiss click, leaving draft saved)
- LINKEDIN_APPLY_BUTTON_SELECTOR matches both "Easy Apply" and
  "Continue applying" (shown when a previous draft exists)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 10:34:19 -08:00
3c46de1358 fix: shadow DOM support — LinkedIn modal is inside shadow root
LinkedIn renders Easy Apply modal inside shadow DOM. document.querySelector()
in evaluate() cannot pierce shadow DOM, but Playwright's page.$() can.

easy_apply.mjs:
- Replaced all frame.evaluate(document.querySelector) with ElementHandle ops
- findModalButton uses modal.$$() + btn.evaluate() instead of evaluateHandle
- getModalDebugInfo uses modal.$eval and modal.$$() for all queries
- dismissModal scans buttons via page.$$() instead of evaluateHandle
- Removed findModalFrame (no longer needed)

form_filler.mjs:
- getLabel() walks up ancestor DOM to find labels (LinkedIn doesn't use label[for])
- Deduplicates repeated label text ("Phone country codePhone country code")
- isRequired() walks ancestors to find labels with * or required indicators

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 10:25:51 -08:00
df31019fd0 fix: modal frame detection — LinkedIn renders Easy Apply in /preload/ frame
LinkedIn renders the Easy Apply modal inside a /preload/ iframe, not the
main document. page.$() searches cross-frame but evaluate() only runs in
the main frame context, causing blank headings/buttons and broken navigation.

- Added findModalFrame() to locate the frame owning the modal dialog
- All evaluate/evaluateHandle calls now use modalFrame instead of page
- findModalButton() and dismissModal() updated to accept frame parameter
- formFiller.fill() receives modalFrame so container scoping works correctly

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 10:20:58 -08:00
0df5eb1b63 debug: add modal structure diagnostics — iframe detection, child tags, html snippet
Logs iframe count, child element tags, and first 500 chars of modal innerHTML
on step 0 to diagnose why buttons/heading aren't being found.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 10:13:22 -08:00
e6fb380e1c fix: cover letter silent drop, submit verification, autocomplete scoping
- Text inputs matched to cover letter now report as unknown if required,
  instead of silently leaving the field empty
- Submit click now verifies modal closed before reporting success;
  returns 'incomplete' with actionable log if modal stays open
- selectAutocomplete scoped to container (modal) to avoid clicking
  wrong dropdowns from the underlying page

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 10:11:44 -08:00
e62756c6ca fix: robustness improvements — atomic writes, timeouts, shell injection, validation errors
- Atomic JSON writes (write-to-tmp + rename) prevent queue/log corruption
- Per-job (3min) and overall run (45min) timeouts prevent hangs
- execFileSync in ai_answer.mjs prevents shell injection with resume paths
- Validation error detection after form fill in Easy Apply modal
- Config-driven enabled_apply_types (from settings.json)
- isRequired() detects required/aria-required/label * patterns
- getLabel() strips trailing * from required field labels
- Actionable logging on failures ("Action: ..." messages)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 10:01:53 -08:00
a7ce119bde fix: resilient modal button detection and form filler robustness
easy_apply.mjs:
- findModalButton() uses 3-strategy detection: aria-label exact/substring,
  then exact button text match — survives LinkedIn aria-label changes
- Check order fixed: Next → Review → Submit (submit only when no forward nav)
- All queries scoped to modal + :not([disabled])
- dismissModal() with fallback chain: Dismiss → Close/X → Escape → Discard
- Uses innerText for button text (ignores hidden children)

form_filler.mjs:
- All queries scoped to container (modal when present, page otherwise)
- Radio labels use $$('label') + textContent instead of broken :has-text()
- Autocomplete uses waitForSelector instead of blind 800ms sleep
- EEO selects iterate options directly (selectOption doesn't accept regex)
- Country code check ordered before country to prevent fragile match order

constants.mjs:
- Add AUTOCOMPLETE_WAIT, AUTOCOMPLETE_TIMEOUT
- Remove unused button selectors (now handled inline by findModalButton)

ai_answer.mjs + keywords.mjs:
- Use ANTHROPIC_API_URL constant, claude-sonnet-4-6 model

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 09:54:44 -08:00
33c50178f4 debug: full-page button scan, modal heading, real progress, validation errors; bump max steps to 20 2026-03-06 17:43:03 +00:00
8556208405 debug: verbose step-by-step logging in easy_apply modal flow 2026-03-06 17:27:39 +00:00
093b349aad fix: waitForSelector on modal after click instead of fixed 1.5s delay 2026-03-06 17:13:56 +00:00
8c626b5147 debug: log apply elements on skip; use state=attached for waitForSelector 2026-03-06 17:05:57 +00:00
318da35e01 fix: scroll page before waiting for Easy Apply button; extend timeout to 12s 2026-03-06 17:04:27 +00:00
45ad5d1ec9 fix: Easy Apply is an <a> not <button> — update selector; wait for element instead of fixed timeout 2026-03-06 17:01:02 +00:00
02a7501e9d fix: use aria-label selectors for LinkedIn Easy Apply button (class names are now hashed) 2026-03-06 16:53:25 +00: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
8212f97aba refactor: normalize apply statuses, remove dead code, fix signal handler
- lib/apply/index.mjs: add STATUS_MAP to normalize platform-specific statuses
  to generic ones (no_button/no_submit/no_modal → skipped_no_apply).
  Documented all generic statuses for AI/developer reference.
- job_applier.mjs: handleResult now handles skipped_no_apply, default case
  logs + saves instead of silently dropping
- lib/linkedin.mjs: remove dead applyLinkedIn() and detectAts(), clean imports
  (~110 lines removed). Search-only module now.
- lib/wellfound.mjs: remove dead applyWellfound(), clean imports.
  Search-only module now.
- lib/lock.mjs: fix async signal handler — shutdown handlers now actually
  complete before process.exit()
- test_linkedin_login.mjs: add try/catch/finally with proper browser cleanup
- README: update status table with all current statuses

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 17:39:48 -08:00
4fb5917c87 fix: restore real Wellfound apply logic in lib/apply/wellfound.mjs (not a stub) 2026-03-06 01:05:35 +00:00
35fdbc487a refactor: handler registry pattern — lib/apply/<ats>.mjs, applyToJob() routes by apply_type 2026-03-06 01:03:11 +00:00