Metadata-Version: 2.5
Name: aihawk
Version: 0.3.0
Summary: Drive a stealth browser with an LLM from one command
Author-email: feder-cr <85809106+feder-cr@users.noreply.github.com>
License-Expression: MIT
License-File: LICENSE
Classifier: Operating System :: Microsoft :: Windows
Classifier: Operating System :: POSIX :: Linux
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Requires-Python: >=3.11
Requires-Dist: click>=8.1
Requires-Dist: invisible-playwright-mcp>=0.10.0
Requires-Dist: mcp>=1.2
Requires-Dist: openai>=1.30
Requires-Dist: starlette>=0.37
Requires-Dist: uvicorn>=0.30
Provides-Extra: test
Requires-Dist: pytest-asyncio>=0.23; extra == 'test'
Requires-Dist: pytest>=8; extra == 'test'
Description-Content-Type: text/markdown

<div align="center">

<picture>
  <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/feder-cr/AIHawk/main/assets/aihawk-logo-dark.png">
  <img alt="AIHawk" src="https://raw.githubusercontent.com/feder-cr/AIHawk/main/assets/aihawk-logo-light.png" width="380">
</picture>

**An AI agent with a real browser. You say what you want in plain language, it goes and does it on the actual web.**

<sub>FEATURED IN</sub><br>
[**Business Insider**](https://www.businessinsider.com/aihawk-applies-jobs-for-you-linkedin-risks-inaccuracies-mistakes-2024-11) ·
[**TechCrunch**](https://techcrunch.com/2024/10/10/a-reporter-used-ai-to-apply-to-2843-jobs/) ·
[**Semafor**](https://www.semafor.com/article/09/12/2024/linkedins-have-nots-and-have-bots) ·
[**Wired**](https://www.wired.it/article/aihawk-come-automatizzare-ricerca-lavoro/) ·
[**The Verge**](https://www.theverge.com/2024/10/10/24266898/ai-is-enabling-job-seekers-to-think-like-spammers) ·
[**Vanity Fair**](https://www.vanityfair.it/article/intelligenza-artificiale-candidature-di-lavoro) ·
[**404 Media**](https://www.404media.co/i-applied-to-2-843-roles-the-rise-of-ai-powered-job-application-bots/)

</div>

---

## Two ways to use it

The only question is where the model comes from.

### 1. You already use an assistant that can run tools

Your assistant brings the model. You add this browser to it, and nothing changes
about how you work.

**Claude Code:**

```bash
claude mcp add -s user stealth -- uvx invisible-playwright-mcp
```

**Codex:**

```bash
codex mcp add stealth -- uvx invisible-playwright-mcp
```

**Gemini CLI:**

```bash
gemini mcp add -s user stealth uvx invisible-playwright-mcp
```

Then ask your assistant, in the window you already have open:

> Go to news.ycombinator.com and give me the top five titles.

Claude Desktop, Cursor, VS Code, Windsurf, Zed and Cline take a config file
instead, and the file is not the same shape for all of them. Each one is
written out in the
[server's README](https://github.com/feder-cr/invisible-playwright-mcp#adding-it-to-your-client).

### 2. You don't, or you want to watch it work

We bring the interface, you bring an [OpenRouter](https://openrouter.ai) key.
Chat on the left, the live browser on the right.

```bash
uvx aihawk ui --openrouter-key sk-or-...
```

Then open **http://127.0.0.1:8765** and type the same thing.

**Same patched Firefox behind both.** AIHawk reaches it through that MCP server,
over MCP, exactly as your assistant would.

---

## Before either one

**Python 3.11 or newer**, on **Windows (x86_64)** or **Linux (x86_64, arm64)**.
macOS is not supported: the last engine build for it was `firefox-20`.

Both commands above start with `uvx`, so you need [uv](https://docs.astral.sh/uv/):

```bash
curl -LsSf https://astral.sh/uv/install.sh | sh              # Linux
powershell -c "irm https://astral.sh/uv/install.ps1 | iex"   # Windows
```

**The browser is a separate download of about a quarter of a gigabyte**, and it
does not arrive when you install either side. It arrives on the first request
that needs a page, so your first instruction sits there for a while and a slow
connection can time out with an error that says nothing about a download. Get it
over with first, where you can watch it:

```bash
uvx invisible-playwright fetch
```

---

## What to ask it

Anything that needs a browser rather than an API, and a person's judgement about
what is on the page.

> 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.

It drives the page the way a person would: the pointer moves, keys are pressed,
and it refuses to set a form field from JavaScript even when that would be
quicker, because a page can tell the difference.

## Options

- **`--openrouter-key`** Your key, or the `OPENROUTER_API_KEY` variable.
- **`--model`** An OpenRouter model id, or `AIHAWK_MODEL`. Defaults to `z-ai/glm-4.6`.
- **`--proxy`** Optional. `http://user:pass@proxy.example.com:8080` or
  `socks5://proxy.example.com:1080`. Host and port are both required. The
  timezone, locale and egress follow it.
- **`--binary`** An engine binary you already have. It must be the build the seal
  pins, or startup refuses: this skips the download, not the version check.
- **`--seed`** An integer. Same seed, same browser identity, every run.
- **`--profile-dir`** A directory to keep the profile in, so logins and cookies
  survive restarts.
- **`--headed`** Show the browser window. The interface shows you the page anyway.
- **`--host`, `--port`** `127.0.0.1` and `8765`. Changing the host
  exposes an interface that has no authentication.

Passing `--openrouter-key` puts the key in your shell history, and on Linux in
the process list. `OPENROUTER_API_KEY` in the environment avoids both.

Either way it does not reach the browser process: it is removed from the
environment the engine starts with, by name and by value, so a copy under a
second name goes too.
[`tests/test_key_isolation.py`](https://github.com/feder-cr/AIHawk/blob/main/tests/test_key_isolation.py)
fails if that stops being true.

## The wiki

The reading room around the agent lives in the
[wiki](https://github.com/feder-cr/AIHawk/wiki): the
[AI browser-agent landscape and its comparisons](https://github.com/feder-cr/AIHawk/wiki/guides-alternatives-and-comparisons),
[what to check when an agent gets blocked](https://github.com/feder-cr/AIHawk/wiki/why-does-my-ai-agent-get-blocked),
and [what happened to OpenAI Operator](https://github.com/feder-cr/AIHawk/wiki/is-openai-operator-still-available),
among others. Worked examples, transcripts and their outputs live in
[articles/](https://github.com/feder-cr/AIHawk/tree/main/articles).

## The rest of the family

- **[invisible-playwright-mcp](https://github.com/feder-cr/invisible-playwright-mcp)**
  The MCP server from option 1. Tools only, no interface.
- **[invisible_playwright](https://github.com/feder-cr/invisible_playwright)**
  The engine, as a Python library, for writing code instead of prompts. The API
  is Playwright's.
- **[invisible_core](https://github.com/feder-cr/invisible_core)**
  Seed to fingerprint to preferences, proxy and geolocation.

## Contributing

Issues and pull requests welcome on whichever of those the problem lives in. If
you are not sure, open it here. See
[CONTRIBUTING](https://github.com/feder-cr/AIHawk/blob/main/.github/CONTRIBUTING.md).

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.

## Using it responsibly

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.

## License

[MIT](https://github.com/feder-cr/AIHawk/blob/main/LICENSE). Everything
distributed before 2 September 2026 was released under AGPL-3.0 and stays under
it.
