7.5 KiB
name, description
| name | description |
|---|---|
| claw-apply | Automated job search and application for LinkedIn and Wellfound. Searches for matching roles hourly, applies automatically every 6 hours using Playwright + Kernel stealth browsers. Handles LinkedIn Easy Apply multi-step modals and Wellfound applications. Self-learning — asks you via Telegram when it hits an unknown question, saves your answer, and never asks again. Retries failed applications automatically. Preview mode lets you review the queue before applying. |
claw-apply
Automated job search and application. Finds matching roles on LinkedIn and Wellfound, applies automatically, and learns from every unknown question.
Requirements
- Node.js 18+
- Kernel account — stealth browsers + bot detection bypass (required)
- Kernel CLI:
npm install -g @onkernel/cli - Telegram bot for notifications (BotFather)
- Anthropic API key (optional — enables AI-enhanced keyword generation)
- OpenClaw (optional — enables auto-scheduling via
setup.mjs)
Note: Playwright is installed automatically via
npm installas a library for browser connectivity. You don't need to install it globally or manage browsers yourself — Kernel handles all browser execution.
Setup
1. Install
git clone https://github.com/MattJackson/claw-apply.git
cd claw-apply
npm install
2. Kernel: proxy + auth sessions
# Log in to Kernel
kernel login
# Create a residential proxy (US recommended for LinkedIn/Wellfound)
kernel proxies create --type residential --country US --name "claw-apply-proxy"
# Note the proxy ID from output
# Create managed auth connections (one per platform)
kernel auth connections create --profile-name "LinkedIn-YourName" --domain linkedin.com
# Note the connection ID from output
kernel auth connections create --profile-name "WellFound-YourName" --domain wellfound.com
# Note the connection ID from output
# Trigger initial login flows (opens a browser URL to complete auth)
kernel auth connections login <linkedin-connection-id>
kernel auth connections login <wellfound-connection-id>
3. Configure
cp config/settings.example.json config/settings.json
cp config/profile.example.json config/profile.json
cp config/search_config.example.json config/search_config.json
settings.json — fill in:
notifications.telegram_user_id— your Telegram user IDnotifications.bot_token— Telegram bot token from BotFatherkernel.proxy_id— proxy ID from step 2kernel.profiles.linkedin— profile name e.g.LinkedIn-YourNamekernel.profiles.wellfound— profile name e.g.WellFound-YourNamekernel.connection_ids.linkedin— connection ID from step 2kernel.connection_ids.wellfound— connection ID from step 2
profile.json — your name, email, phone, resume path, work authorization, salary targets
search_config.json — keywords, platforms, location filters, salary filters, exclusions
4. Verify
export KERNEL_API_KEY=your_kernel_api_key
export ANTHROPIC_API_KEY=your_anthropic_api_key # optional, for AI keywords
node setup.mjs
Setup will:
- Validate all config files
- Send a Telegram test message
- Test LinkedIn + Wellfound logins
- Auto-register cron jobs (if
OPENCLAW_GATEWAY_URL+OPENCLAW_GATEWAY_TOKENare set)
5. Run manually
node job_searcher.mjs # search now
node job_applier.mjs --preview # preview queue without applying
node job_applier.mjs # apply now
node status.mjs # show queue + run status
6. Schedule
Via OpenClaw — set env vars before running setup.mjs:
export OPENCLAW_GATEWAY_URL=http://localhost:3000
export OPENCLAW_GATEWAY_TOKEN=your_gateway_token
node setup.mjs # registers crons automatically
Crons registered:
- Searcher:
0 * * * *(hourly), no timeout, enabled by default - Applier:
0 */6 * * *(every 6h), no timeout, disabled by default — enable when ready
Via system cron — add to crontab:
0 * * * * cd /path/to/claw-apply && KERNEL_API_KEY=xxx node job_searcher.mjs >> /tmp/searcher.log 2>&1
0 */6 * * * cd /path/to/claw-apply && KERNEL_API_KEY=xxx node job_applier.mjs >> /tmp/applier.log 2>&1
How it works
Search — runs your keyword searches on LinkedIn and Wellfound, paginates through results, inline-classifies each job (Easy Apply vs external ATS), filters exclusions, deduplicates, and queues new jobs. First run searches 90 days back; subsequent runs search 2 days.
Apply — picks up queued jobs sorted by priority (Easy Apply first), opens stealth browser sessions, fills forms using your profile + learned answers, and submits. Auto-refreshes Kernel auth sessions if login expires. Retries failed jobs (default 2 retries).
Learn — on unknown questions, messages you on Telegram. You reply, the answer is saved to answers.json with regex pattern matching, and the job is retried next run.
Lockfile — prevents parallel runs. If searcher is running, a second invocation exits immediately.
File structure
claw-apply/
├── job_searcher.mjs Search agent
├── job_applier.mjs Apply agent
├── setup.mjs Setup wizard + cron registration
├── status.mjs Queue + run status report
├── lib/
│ ├── browser.mjs Kernel stealth browser factory
│ ├── session.mjs Auth session refresh via Kernel API
│ ├── linkedin.mjs LinkedIn search + Easy Apply
│ ├── wellfound.mjs Wellfound search + apply
│ ├── form_filler.mjs Form filling with pattern matching
│ ├── queue.mjs Job queue + config management
│ ├── keywords.mjs AI-enhanced keyword generation
│ ├── lock.mjs PID lockfile + graceful shutdown
│ ├── notify.mjs Telegram notifications
│ ├── search_progress.mjs Per-platform search resume tracking
│ ├── constants.mjs Shared constants + ATS patterns
│ └── apply/
│ ├── index.mjs Handler registry
│ ├── easy_apply.mjs LinkedIn Easy Apply (full)
│ ├── wellfound.mjs Wellfound apply (full)
│ ├── greenhouse.mjs Greenhouse (stub)
│ ├── lever.mjs Lever (stub)
│ ├── workday.mjs Workday (stub)
│ ├── ashby.mjs Ashby (stub)
│ └── jobvite.mjs Jobvite (stub)
├── config/
│ ├── *.example.json Templates (committed)
│ └── *.json Your config (gitignored)
└── data/ Runtime data (gitignored, auto-managed)
answers.json — self-learning Q&A
When the applier can't answer a question, it messages you on Telegram. Your reply is saved and reused forever:
[
{ "pattern": "quota attainment", "answer": "1.12" },
{ "pattern": "years.*enterprise", "answer": "5" },
{ "pattern": "1.*10.*scale", "answer": "9" }
]
Patterns are matched case-insensitively and support regex. First match wins.
ATS support
| Platform | Status |
|---|---|
| LinkedIn Easy Apply | ✅ Full |
| Wellfound | ✅ Full |
| Greenhouse | 🚧 Stub |
| Lever | 🚧 Stub |
| Workday | 🚧 Stub |
| Ashby | 🚧 Stub |
| Jobvite | 🚧 Stub |
External ATS jobs are queued and classified — stubs will be promoted to full implementations based on usage data.