@bagasek/firehh
v0.4.0
Published
Bun-powered HeadHunter CLI for vacancy search, resume checks, OAuth tokens, and applications.
Readme
firehh
firehh is a Bun/TypeScript CLI for hh.ru (HeadHunter): OAuth login, resume listing, vacancy search/view, application status, and applying with a cover letter. It is designed for humans and AI agents: scoped commands, deterministic stdout/stderr, compact JSON envelopes, actionable errors, and complete --help at every level.
Install / build
bun install -g @bagasek/firehh
firehh --helpFrom this repository:
bun install
bun src/firehh.ts --help
bun test
bun run check60-second quickstart
# 1) Login once. Opens Chrome/Chromium and stores a token.
firehh auth login
# stdout: {"ok":true,"data":{"token_file":"/Users/me/.config/firehh/token.json","expires_at":"2026-07-05T12:00:00.000Z","credential_source":"android","auth_flow":"browser"}}
# 2) Pick a resume id.
firehh resumes list
# stdout: {"ok":true,"data":{"found":1,"items":[{"id":"resume-abc","title":"Frontend Developer","status":"published","area":"Москва","updated_at":"2026-06-30T10:00:00+0300","total_experience_months":72}]}}
# 3) Search vacancies.
firehh vacancies search --text "frontend react" --remote --must "React|TypeScript|Next" --reject "Vue|Angular|React Native" --per-page 5
# stdout: {"ok":true,"data":{"found":120,"matched_items":3,"page":0,"pages":24,"per_page":5,"items":[{"id":"133561763","name":"Frontend React Developer","employer":{"id":"1","name":"Example"},"url":"https://hh.ru/vacancy/133561763",...}]}}
# 4) Inspect one vacancy before applying.
firehh vacancies view 133561763
# stdout: {"ok":true,"data":{"id":"133561763","name":"Frontend React Developer","active":true,"already_applied":false,"description_text":"...","apply_url":"https://hh.ru/applicant/vacancy_response?vacancyId=133561763",...}}
# 5) Dry-run an application payload, then send it.
firehh applications apply 133561763 --resume resume-abc --message-file cover-letter.txt --dry-run
firehh applications apply 133561763 --resume resume-abc --message-file cover-letter.txt
# stdout: {"ok":true,"data":{"applied":true,"negotiation_id":"987654","location":"https://api.hh.ru/negotiations/987654"}}Command reference
Run firehh --help, firehh <scope> --help, or firehh <scope> <command> --help. A bare scope such as firehh vacancies prints that scope's help and exits 0.
auth
firehh auth login [--timeout <seconds>] [--browser <path>]
firehh auth statusauth loginopens Chrome/Chromium, captures thehhandroid://oauthresponseredirect via DevTools, exchanges the code, and saves the token. Output data:token_file,expires_at,credential_source,auth_flow.auth statusprints token metadata only:token_file,token_source,token_source_file,access_token,refresh_token,expires_at.
resumes
firehh resumes list
firehh resumes for-vacancy <vacancy-id>resumes listoutput data:found,items:[{id,title,status,area,updated_at,total_experience_months}].resumes for-vacancychecks that the vacancy is remote and from an accredited IT employer, then returnsvacancy,already_applied,found,items.
vacancies
firehh vacancies view <vacancy-id>
firehh vacancies search [--text <query>] [--area <id>] [--remote] [--hybrid] [--employment <id>] [--experience <id>] [--salary-from <n>] [--salary-to <n>] [--only-accredited] [--published-after <date>] [--must <regex>] [--reject <regex>] [--format json|tsv|md|jsonl] [--page <n>] [--per-page <n>]
firehh vacancies suitable <resume-id> [--page <n>] [--per-page <n>]vacancies viewoutput data includes normalizedactive,archived,salary.gross,contacts,apply_url,already_applied,description_text, andformalization.{labor_contract,gph,project_work,individual_entrepreneur}.vacancies searchuses hh.ru API filters plus local filters for salary bounds, accreditation,--must, and--reject. Defaults:--format json,--page 0,--per-page 20. JSON output data:found,matched_items,page,pages,per_page,items.--format tsv|md|jsonlwrites raw rows to stdout without the{ok}envelope.vacancies suitablesearches hh.ru suitable vacancies by resume id. Output data:resume_id,query,found,page,pages,per_page,items.
applications
firehh applications apply <vacancy-id> --resume <resume-id> (--message <text> | --message-file <path>) [--dry-run]
firehh applications status <vacancy-id>
firehh applications list [--since <date>] [--page <n>] [--per-page <n>]applications applysends the requested vacancy, resume, and cover letter to hh.ru without local eligibility checks.--dry-runoutput data:dry_run,vacancy_id,resume_id,cover_letter_chars,cover_letter; real apply output data:applied,negotiation_id,location.applications statusoutput data:vacancy_id,already_applied,items.applications listoutput data:since,page,per_page,scanned_pages,items:[{id,created_at,updated_at,state,employer_state,has_updates,viewed_by_opponent,vacancy,resume}]. Defaults:--page 0,--per-page 50.
Output contract and errors
- Success JSON stdout:
{"ok":true,"data":...}. - Error JSON stderr:
{"ok":false,"error":{"code":"...","message":"..."}}. - Interactive prompts, browser progress, diagnostics, and update notices go to stderr.
- Help/version are text on stdout.
- Exception:
firehh vacancies search --format tsv|md|jsonlwrites raw machine-friendly rows to stdout.
Exit codes:
0: success, help, or version.1: input/usage error (INPUT_ERROR) such as unknown command, unknown flag, missing required arg, invalid date/number/regex.2: auth, browser, hh.ru API, or runtime failure (AUTH_ERROR,HH_ERROR,hh_<status>,captcha,direct_response).
Important error codes:
INPUT_ERROR: fix invocation; rerun the command with--help.AUTH_ERROR/ missing token message: runfirehh auth login.captcha: stderr includescaptcha_url; open it, solve it, then retry the same command.direct_response: hh.ru requires manual apply; open returnedurl.hh_<status>: hh.ru API returned HTTP status; message is compacted from the API response.
Authentication
The CLI includes hh.ru Android OAuth credentials, so login works without a project .env:
firehh auth loginToken lookup precedence:
HH_ACCESS_TOKENorHH_TOKENenvironment variable. The value may be a raw access token or a JSON token object.- Token file at
~/.config/firehh/token.json, orHH_TOKEN_FILEwhen set.
Stored tokens are refreshed automatically when expires_at is near and refresh_token exists. If refresh is impossible, run firehh auth login again.
Optional environment overrides:
HH_CLIENT_ID=
HH_CLIENT_SECRET=
HH_REDIRECT_URI=hhandroid://oauthresponse
HH_TOKEN_FILE=
HH_ACCESS_TOKEN=Using with AI agents
firehh is agent-friendly beyond “it returns JSON”:
--helpis self-contained: commands, flags, defaults, output shape, exit codes, and examples.- Every command is scoped (
auth,resumes,vacancies,applications), so discovery is predictable. - Unknown commands/flags include nearest alternatives and a concrete help command.
- Stdout is reserved for data; stderr is reserved for prompts/errors/diagnostics.
- Exit code
1means the agent should fix its invocation; exit code2means auth/hh.ru/browser state needs attention.
Examples:
firehh vacancies search --text "frontend react" --remote \
| jq -r '.data.items[] | [.id, .name, .employer.name, .url] | @tsv'
if ! out=$(firehh resumes list 2>err.json); then
jq -r '.error.message' err.json >&2
fiPlatform notes
- Runtime: Bun.
- Browser OAuth: Chrome/Chromium with DevTools; tested primarily on macOS.
- DOCX cover letters: converted with macOS
textutil. On non-macOS, convert DOCX to.txtyourself or pass--message.
