Skip to content

Commit e64529d

Browse files
committed
Cut the README down to what somebody has to do
It was explaining instead of instructing. Two mermaid diagrams asked the reader to understand an architecture before running anything, and a whole section argued why the browser gets challenged less, which is the browser project's material rather than this one's. Neither told anyone what to type. The file is now organised around what the reader came for, documents or a browser his AI drives, and he reaches the first command 27 lines in. Half the length, 13496 bytes down to 7145. Nothing was traded away for the shorter version. Still findable: there is no captcha solver, nothing fills and submits an application end to end, only OpenAI works, and the MCP server is a package rather than something in this repository.
1 parent 0b0c16f commit e64529d

1 file changed

Lines changed: 54 additions & 207 deletions

File tree

README.md

Lines changed: 54 additions & 207 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,7 @@
22

33
# AIHawk
44

5-
**Two halves of the same idea: an LLM that writes your application, and a browser that reaches the end of a task you drive it through.**
6-
7-
The first half runs from this repository today. The second ships as installable packages you attach to your AI client.
5+
**An LLM that writes your application documents, and a browser your AI client drives.**
86

97
[**Business Insider**](https://www.businessinsider.com/aihawk-applies-jobs-for-you-linkedin-risks-inaccuracies-mistakes-2024-11) ·
108
[**TechCrunch**](https://techcrunch.com/2024/10/10/a-reporter-used-ai-to-apply-to-2843-jobs/) ·
@@ -19,46 +17,11 @@ The first half runs from this repository today. The second ships as installable
1917

2018
---
2119

22-
## Where this stands
23-
24-
AIHawk went viral in 2024 as a bot that applied to jobs for you. That is the
25-
version the press above wrote about, and the mass-application engine is no longer
26-
in this repository.
27-
28-
It was removed for a reason worth stating plainly. Firing applications at a job
29-
board is the easy half and the worthless half. It stops working the moment the
30-
site decides you are a machine, and it produces the kind of volume that made
31-
recruiters hate the whole category. The half that was actually hard, and that
32-
survived, is a browser that reaches the end of a task without being stopped.
33-
34-
So AIHawk is two things now, and this README is careful about which is which:
35-
36-
**In this repository, runnable today:** the document half. An LLM reads your
37-
profile and a job posting and writes a resume or a cover letter for that specific
38-
posting. It is a local command line tool, it works, and CI checks it on every
39-
push.
40-
41-
**As separate installable packages:** the browser half. A Firefox patched at the
42-
source, exposed to any AI client over MCP. You install it with one command and
43-
never clone anything.
20+
The 2024 mass-application bot the press wrote about is not in this repository any more. It is still in the git history if you forked it and want to compare, but it is unmaintained and will not work against anything current. What is here now, and what installs beside it, is below.
4421

45-
What joins them into one automated pipeline does not exist yet. That part is
46-
honest roadmap, described at the bottom, not shipped code.
22+
## I want documents
4723

48-
## The document half, in this repository
49-
50-
Generate a resume, a resume rewritten for a specific job posting, or a cover
51-
letter for a specific job posting.
52-
53-
```mermaid
54-
flowchart LR
55-
PR["Your profile<br/>plain_text_resume.yaml"] --> G["main.py<br/><i>LLM + HTML/CSS templates</i>"]
56-
JD["A job posting URL<br/><i>opened and scraped in Chrome</i>"] --> G
57-
G --> CV["PDF in data_folder/output/"]
58-
```
59-
60-
Python 3.12, and Chrome installed. It opens the job posting you point it at,
61-
and it renders the finished HTML to PDF.
24+
You need **Python 3.12** and **Chrome** installed.
6225

6326
```bash
6427
git clone https://github.com/feder-cr/Jobs_Applier_AI_Agent_AIHawk.git
@@ -70,39 +33,37 @@ pip install -r requirements.txt
7033
python main.py
7134
```
7235

73-
The first run creates `data_folder/` for you, copied from the worked example, and
74-
stops to say so. Open the three files it made and put your own details in:
75-
`plain_text_resume.yaml` is your experience and skills, `work_preferences.yaml`
76-
is what you are looking for, and `secrets.yaml` takes your OpenAI API key. Those
77-
files are not in git, so what you write there stays yours.
36+
That first run creates `data_folder/` from `data_folder_example/`, tells you which files to edit, and stops. Edit these three:
37+
38+
| File | Put in it |
39+
|---|---|
40+
| `data_folder/plain_text_resume.yaml` | Your experience and skills |
41+
| `data_folder/work_preferences.yaml` | What you are looking for |
42+
| `data_folder/secrets.yaml` | Your OpenAI API key |
43+
44+
They are git-ignored, so what you write there stays yours.
45+
46+
Run it again and pick one:
47+
48+
- **Generate Resume**
49+
- **Generate Resume Tailored for Job Description**
50+
- **Generate Tailored Cover Letter for Job Description**
7851

79-
Run it again and it asks which of the three documents you want, then writes the
80-
PDF into `data_folder/output/`.
52+
The two tailored options ask for a job posting URL and open it in Chrome. The finished PDF lands in `data_folder/output/`.
8153

82-
It calls OpenAI, on `gpt-4o-mini`. Other providers used to be wired up through
83-
LangChain and are not any more: the code that offered the choice was removed with
84-
the mass-application engine, so an Anthropic or Ollama key in `secrets.yaml` gets
85-
handed to an OpenAI client and fails. Put an OpenAI key there, and keep it out of
86-
your commits. There is a test in here that fails the build if a key ever lands in
87-
that file again, because one did.
54+
**Use an OpenAI key.** This calls `gpt-4o-mini`, and no other provider works: an Anthropic or Ollama key in `secrets.yaml` gets handed to an OpenAI client and fails.
8855

89-
## The browser half, as packages
56+
## I want a browser my AI drives
9057

91-
This is the part that installs rather than clones, and none of it lives in this
92-
repository.
58+
It installs as a package, `invisible-playwright-mcp` on PyPI. None of it is in this repository, so there is nothing to clone here.
9359

94-
MCP is the protocol AI clients use to attach external tools. If you use
95-
[Claude Code](https://claude.com/claude-code), Claude Desktop, Cursor or
96-
anything similar, you already have a client. You need
97-
[uv](https://docs.astral.sh/uv/) for the `uvx` command, Python 3.11 or newer, and
98-
Windows or Linux.
60+
You need [uv](https://docs.astral.sh/uv/) for the `uvx` command, **Python 3.11 or newer**, and **Windows or Linux**. If you use [Claude Code](https://claude.com/claude-code), Claude Desktop, Cursor or anything similar, you already have the client half.
9961

10062
```bash
10163
claude mcp add stealth --env STEALTHFOX_PROXY=http://user:pass@host:port -- uvx invisible-playwright-mcp
10264
```
10365

104-
Without a proxy, drop the `--env` flag. For any other client, the same thing as a
105-
config entry:
66+
Drop the `--env` flag if you are not using a proxy. In any other client, the same thing as a config entry:
10667

10768
```json
10869
{
@@ -120,166 +81,52 @@ config entry:
12081
}
12182
```
12283

123-
Use an absolute path for the profile directory. Most clients pass env values
124-
through verbatim, so a leading `~` becomes a folder literally named `~`.
84+
Restart the client and list your MCP servers. `stealth` should be there with thirteen tools:
12585

126-
### Check it worked
86+
**Pages:** `session_new_page`, `session_list_pages`, `session_select_page`, `session_close_page`
87+
**Reading:** `browser_navigate`, `browser_read_text`, `browser_snapshot`, `browser_take_screenshot`
88+
**Acting:** `browser_click`, `browser_click_at`, `browser_type`, `browser_press_key`, `browser_evaluate`
12789

128-
Restart the client and list your MCP servers. `stealth` should be there with
129-
thirteen tools attached. Then type one small thing before you type a big one:
90+
You never call those yourself, the model does. Ask it for one small thing first, like opening a page and taking a screenshot.
13091

131-
> Use the stealth browser to open a browser fingerprint check page, take a
132-
> screenshot, and tell me the timezone, locale, languages and public IP it
133-
> reports.
134-
135-
Read those four values against what your own browser reports on the same page. On
136-
a proxied run they should describe the exit country and agree with each other,
137-
not with the machine you are sitting at. That is the whole thesis, checkable in
138-
one prompt, before you trust it with anything that matters.
139-
140-
The first launch downloads the patched Firefox build, a few hundred megabytes, so
141-
it takes a minute and looks like a hang. It is cached afterwards. If the server
142-
does not appear at all, `uvx` is not on the PATH the client sees, which is not
143-
always the PATH your shell sees.
92+
**The first launch downloads a few hundred megabytes** and looks like a hang. It is cached after that. If the server never appears at all, `uvx` is not on the PATH your client sees, which is not always the PATH your shell sees.
14493

14594
### Two things to ask it
14695

147-
The server drives the browser and only the browser: no disk access, on purpose.
148-
Reading a file or writing a CSV is your client's own file tools.
96+
The server drives the browser and nothing else: it has no disk access. Reading and writing files is your client's own tooling.
97+
98+
> Go to `<paste the URL>`. Filter to backend roles in Europe posted in the last two weeks. For each result, open the posting and pull out the title, the location, whether it says remote or hybrid, and anything about visa sponsorship. Give me the list as a table, and append a row to shortlist.csv for each one.
14999
150-
> Go to this careers page: `<paste the URL>`. Filter to backend roles in Europe
151-
> posted in the last two weeks. For each result, open the posting and pull out the
152-
> title, the location, whether it says remote or hybrid, and anything it says
153-
> about visa sponsorship. Give me the list as a table, and append a row to
154-
> shortlist.csv for each one: company, role, URL, date.
100+
> Go to `<paste the URL>`. One way, Milan to Lisbon, economy, one checked bag, one adult. Check every date from the 12th to the 16th of next month, one at a time, and read the cheapest fare for each day. The date field is a calendar widget, so click the days rather than typing them. If a date has no availability say so, do not guess a number. Write the five to flights.md sorted by price, and leave the cheapest open in a tab.
155101
156-
> Go to this flight search site: `<paste the URL>`. One way, Milan to Lisbon,
157-
> economy, one checked bag, one adult. Check every date from the 12th to the 16th
158-
> of next month, one at a time, and read the cheapest fare for each day. The date
159-
> field is a calendar widget, so click the days rather than typing them. If a date
160-
> has no availability say so, do not guess a number. Write the five to flights.md
161-
> sorted by price, and leave the cheapest open in a tab so I can book it myself.
102+
### Settings
162103

163-
Those two share nothing except the machinery: same server, same browser, same
164-
thirteen tools, nothing reconfigured in between.
104+
| Variable | What it does |
105+
|---|---|
106+
| `STEALTHFOX_PROXY` | `http://user:pass@host:port`, or `socks5://`. Credentials go in the URL. |
107+
| `STEALTHFOX_SEED` | An integer. Same seed, same browser identity, every run. Set one for anything you run more than once. |
108+
| `STEALTHFOX_PROFILE_DIR` | Absolute path. Persistent profile, so logins and cookies survive restarts. Most clients pass env values through verbatim, so a leading `~` becomes a folder literally named `~`. |
109+
| `STEALTHFOX_HEADLESS` | Headless by default. Set `0` to watch the window, worth doing the first few times. |
165110

166-
## Why fewer challenges appear
111+
## Limits
167112

168-
An anti-bot rarely catches you on one exotic value. It catches you on a
169-
contradiction. Put a proxy in front of an ordinary browser and you have built one:
170-
the address says one country, the clock and the language say another.
113+
**There is no captcha solver here.** None is built in and none is wired to a third party service.
171114

172-
```mermaid
173-
flowchart LR
174-
A["Proxy says: United States"] --> D{"Anti-bot compares"}
175-
B["Browser says: Italy"] --> D
176-
D --> E["Contradiction<br/>→ challenge"]
177-
```
115+
**Nothing joins the two halves yet.** No code in any of these repositories fills in an application form and submits it end to end. The generated PDF is something you upload yourself, and the browser is something you drive through prompts. There is no timeline attached to that.
178116

179-
The engine turns that around. Before the browser starts, the exit IP is resolved
180-
through the proxy, and everything the browser will declare is derived from that
181-
exit rather than from the machine the process happens to be running on. Values an
182-
IP cannot imply, like screen metrics and GPU strings, come from
183-
`STEALTHFOX_SEED` and stay put.
184-
185-
```mermaid
186-
flowchart LR
187-
P["Proxy exit IP"] --> C["invisible_core derives<br/>timezone · locale · languages<br/>geolocation · fonts · WebRTC"]
188-
C --> B["The browser declares all of it"]
189-
P --> D{"Anti-bot compares"}
190-
B --> D
191-
D --> E["Everything agrees<br/>→ nothing to raise a challenge about"]
192-
```
117+
## The other repositories
193118

194-
There is nothing incoherent in that declared layer to find, because it all comes
195-
from one source. That is one input into a risk score among several, and it is the
196-
input this project can do something about. The practical consequence: many of the
197-
interstitials and hard blocks people expect from browser automation never appear,
198-
because nothing raised them.
119+
- [invisible_playwright](https://github.com/feder-cr/invisible_playwright) - the Python wrapper and the browser it pins and drives. Use this directly if you would rather script than prompt: the API is Playwright's.
120+
- [invisible-playwright-mcp](https://github.com/feder-cr/invisible-playwright-mcp) - the MCP server installed above.
121+
- [invisible_core](https://github.com/feder-cr/invisible_core) - seed to fingerprint to preferences, proxy and geolocation derivation.
122+
- [lib_resume_builder_AIHawk](https://github.com/feder-cr/lib_resume_builder_AIHawk) - the resume rendering library this repository depends on.
199123

200-
**There is no captcha solver in this project.** No third party service wired up,
201-
no pass rate to promise. Cloudflare, Turnstile, DataDome, Kasada, Akamai,
202-
reCAPTCHA and hCaptcha all exist and all still work. What changes is how often
203-
you meet one. When an interactive challenge does appear, `browser_click_at` gives
204-
your client a real pointer: viewport coordinates, a pointer that moves rather than
205-
teleports, press and hold, and a screenshot of what happened. That covers sliders
206-
and press-and-hold widgets. It will not identify motorbikes for you, and sometimes
207-
the honest answer to a challenge is to stop.
124+
Issues and pull requests are welcome on whichever of those the problem lives in. If you are not sure, open it here. When something fails on a page, say which step, what the page did, what the tool returned and which exit country you were on. "It got blocked" is not something anyone can act on.
208125

209-
### Configuration
126+
## Using it
210127

211-
| Variable | What it does |
212-
|---|---|
213-
| `STEALTHFOX_PROXY` | `http://user:pass@host:port`, or `socks5://`. Timezone, locale, languages, geolocation and egress all derive from it. |
214-
| `STEALTHFOX_SEED` | An integer. Fixes the seeded half of the identity: fonts, screen, GPU strings. Same seed, same values, every run. |
215-
| `STEALTHFOX_PROFILE_DIR` | Absolute path. Persistent profile, so logins and cookies survive restarts. |
216-
| `STEALTHFOX_HEADLESS` | Headless by default. Set `0` to watch it work, worth doing the first few times. |
217-
| `STEALTHFOX_BINARY` | A build of your own. Only for people building the engine themselves, and it needs a matching seal file or the launch is refused on purpose. |
218-
219-
Set a seed for anything you run more than once. An identity that changes shape
220-
between sessions is its own kind of signal.
221-
222-
### The thirteen tools
223-
224-
**Pages:** `session_new_page`, `session_list_pages`, `session_select_page`,
225-
`session_close_page`.
226-
**Reading:** `browser_navigate`, `browser_read_text`, `browser_snapshot`,
227-
`browser_take_screenshot`.
228-
**Acting:** `browser_click`, `browser_click_at`, `browser_type`,
229-
`browser_press_key`, `browser_evaluate`.
230-
231-
You do not call these, the model does. Two are worth explaining.
232-
`browser_snapshot` returns the visible interactive elements rather than the
233-
accessibility tree, because one country dropdown contributes roughly two hundred
234-
option nodes to a full tree and buries the form the model was looking for.
235-
`browser_evaluate` runs arbitrary JavaScript and is the escape hatch, worth being
236-
careful with, since a model that reaches for it too early writes a scraper where
237-
reading the page would have done.
238-
239-
## What is not built yet
240-
241-
Said plainly, because the gap between the two halves above is the whole roadmap
242-
and it would be easy to imply it is already closed.
243-
244-
Filling an application form end to end needs a layer that does not exist in any
245-
of these repositories: reading a page into a list of questions each carrying the
246-
address of the element that answers it, and a set of per-platform adapters for
247-
the handful of applicant tracking systems that behave differently from ordinary
248-
HTML. Until that exists, the generated PDF is something you upload yourself, and
249-
the browser is something you drive through prompts.
250-
251-
Nothing here pretends otherwise, and there is no timeline attached.
252-
253-
## The pieces
254-
255-
- [invisible_playwright](https://github.com/feder-cr/invisible_playwright) - the
256-
Python wrapper and the patched Firefox it pins and drives. If you would rather
257-
script than prompt, use this directly: the API is Playwright's.
258-
- [invisible-playwright-mcp](https://github.com/feder-cr/invisible-playwright-mcp) -
259-
the MCP server installed above.
260-
- [invisible_core](https://github.com/feder-cr/invisible_core) - seed to
261-
fingerprint to preferences, proxy and geolocation derivation. The part that
262-
decides what the browser declares.
263-
- [lib_resume_builder_AIHawk](https://github.com/feder-cr/lib_resume_builder_AIHawk) -
264-
the resume rendering library this repository depends on.
265-
266-
Issues and pull requests are welcome on whichever of those the problem lives in.
267-
If you are not sure, open it here. When something fails on a specific site,
268-
describe the shape of it: which step, what the page did, what the tool returned,
269-
which exit country. "It got blocked" is not something anyone can act on, and a
270-
list of targets in the issue tracker helps nobody.
271-
272-
The 2024 command line applier is still in the git history if you forked it and
273-
want to compare. It is not maintained and it will not work against anything
274-
current. Third-party provider plugins are not in this repository: the core is
275-
open source, the plugins were removed for copyright reasons.
276-
277-
## A note on use
278-
279-
This automates a browser under your control. Read the terms of the sites you
280-
point it at, respect their rate limits, and do not use it to submit things a
281-
human has not read.
128+
This automates a browser under your control. Read the terms of the sites you point it at, respect their rate limits, and do not submit anything a human has not read.
282129

283130
## License
284131

285-
See [LICENSE](LICENSE).
132+
See [LICENSE](LICENSE).

0 commit comments

Comments
 (0)