Files
claw-apply/lib/apply/index.mjs
Matthew Jackson 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

87 lines
2.7 KiB
JavaScript

/**
* index.mjs — Apply handler registry
* Maps apply_type → handler module
* To add a new ATS: create lib/apply/<name>.mjs and add one line here
*/
import * as easyApply from './easy_apply.mjs';
import * as greenhouse from './greenhouse.mjs';
import * as lever from './lever.mjs';
import * as workday from './workday.mjs';
import * as ashby from './ashby.mjs';
import * as jobvite from './jobvite.mjs';
import * as wellfound from './wellfound.mjs';
const ALL_HANDLERS = [
easyApply,
greenhouse,
lever,
workday,
ashby,
jobvite,
wellfound,
];
// Build registry: apply_type → handler
const REGISTRY = {};
for (const handler of ALL_HANDLERS) {
for (const type of handler.SUPPORTED_TYPES) {
REGISTRY[type] = handler;
}
}
/**
* Get handler for a given apply_type
* Returns null if not supported
*/
export function getHandler(applyType) {
return REGISTRY[applyType] || null;
}
/**
* List all supported apply types
*/
export function supportedTypes() {
return Object.keys(REGISTRY);
}
/**
* Status normalization — handlers return platform-specific statuses,
* this map converts them to generic statuses that job_applier.mjs understands.
*
* Generic statuses (what handleResult expects):
* submitted — application was submitted successfully
* needs_answer — blocked on unknown form question, sent to Telegram
* skipped_recruiter_only — LinkedIn recruiter-only listing
* skipped_external_unsupported — external ATS not yet implemented
* skipped_no_apply — no apply button/modal/submit found on page
* skipped_honeypot — honeypot question detected, application abandoned
* stuck — modal progress stalled after retries
* incomplete — ran out of modal steps without submitting
*
* When adding a new handler, return any status you want — if it doesn't match
* a generic status above, add a mapping here so job_applier doesn't need to change.
*/
const STATUS_MAP = {
no_button: 'skipped_no_apply',
no_submit: 'skipped_no_apply',
no_modal: 'skipped_no_apply',
};
/**
* Apply to a job using the appropriate handler
* Returns result object with normalized status
*/
export async function applyToJob(page, job, formFiller) {
const handler = getHandler(job.apply_type);
if (!handler) {
return {
status: 'skipped_external_unsupported',
meta: { title: job.title, company: job.company },
externalUrl: job.apply_url || '',
ats_platform: job.apply_type || 'unknown',
};
}
const result = await handler.apply(page, job, formFiller);
return { ...result, status: STATUS_MAP[result.status] || result.status };
}