mirror of
https://github.com/feder-cr/Jobs_Applier_AI_Agent.git
synced 2026-11-07 02:35:57 -10:00
Open-source basics: CONTRIBUTING, the plugin guide, SECURITY, issue and pull request templates, CHANGELOG
This commit is contained in:
1 parent
d1c3e2d0e6
commit
9094020f7f
9 files changed
+293
No files matched your search
@@ -0,0 +1,26 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: AIHawk crashed, or did something other than what the README says
|
||||
title: ""
|
||||
---
|
||||
|
||||
**Do not paste personal data.** No CV, name, e-mail address, phone number, sign-in or API key. Replace them with invented ones. If the bug is a security problem, do not file it here: see SECURITY.md.
|
||||
|
||||
**What happened**
|
||||
|
||||
**What you expected**
|
||||
|
||||
**Steps to reproduce**
|
||||
|
||||
1.
|
||||
2.
|
||||
|
||||
**Version and system**
|
||||
|
||||
- `aihawk --version`:
|
||||
- Python version:
|
||||
- Operating system:
|
||||
|
||||
**Log or error text**
|
||||
|
||||
Paste the message from the terminal. Remove anything personal first.
|
||||
@@ -0,0 +1,8 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: Questions and ideas
|
||||
url: https://github.com/feder-cr/Jobs_Applier_AI_Agent_AIHawk/discussions
|
||||
about: Ask how AIHawk works, or talk over an idea before you build it.
|
||||
- name: Security problem
|
||||
url: https://github.com/feder-cr/Jobs_Applier_AI_Agent_AIHawk/security/policy
|
||||
about: Report it privately. Do not open a public issue for it.
|
||||
@@ -0,0 +1,15 @@
|
||||
---
|
||||
name: Plugin request
|
||||
about: Ask for AIHawk to apply on a job site it does not know yet
|
||||
title: "Plugin: "
|
||||
---
|
||||
|
||||
**Do not paste personal data.** No CV, name, e-mail address or sign-in.
|
||||
|
||||
**Site name and address** (the site or the careers pages it hosts, such as `jobs.example.com`):
|
||||
|
||||
**An example posting URL**, public, that AIHawk should be able to apply on:
|
||||
|
||||
**What the application form is like**: one page or several steps, whether the form is on the posting or at another address, any captcha or sign-in.
|
||||
|
||||
**Do you have a plugin already?** A plugin is a Markdown file that tells the model how the site's form works. See docs/plugins.md. If you wrote one, say whether you tried it on a real posting and what the form did. A plugin is welcome as a pull request, once it has been tried.
|
||||
@@ -0,0 +1,25 @@
|
||||
---
|
||||
name: Site not working
|
||||
about: AIHawk cannot fill, or wrongly fills, a job site's form
|
||||
title: "Site not working: "
|
||||
---
|
||||
|
||||
**Do not paste personal data.** The posting's page and the form may hold a real person's details, and so may the log. Remove them, or replace them with invented ones, before you post. Do not paste a CV, a sign-in or an API key.
|
||||
|
||||
**Site** (the site's name, such as Lever or Greenhouse, and the plugin's file name if you know it):
|
||||
|
||||
**Posting URL** (the public link to the job; if it needs a sign-in, say so instead of pasting it):
|
||||
|
||||
**Form URL**, if it is different from the posting (for example the address that ends in `/apply`):
|
||||
|
||||
**What AIHawk did**
|
||||
|
||||
Say which step went wrong: the fields it filled, the question it asked, the "blocked" message, or the send that did not happen.
|
||||
|
||||
**What the form should have done**
|
||||
|
||||
**The log or the screen text**
|
||||
|
||||
Paste what AIHawk showed. Remove personal data first.
|
||||
|
||||
**Did you try the plugin on this site before?** Yes / No. If you wrote a plugin for it, paste its file here, without personal data.
|
||||
@@ -0,0 +1,13 @@
|
||||
## What this changes and why
|
||||
|
||||
## How it was tested
|
||||
|
||||
- [ ] `ruff check .`
|
||||
- [ ] `pytest -m "not browser"`
|
||||
- [ ] `pytest -m browser`, if the change touches the browser, the forms or `apply.py`
|
||||
- [ ] For a plugin: tried on a real posting's form. Say which site, and what the form did, including whether the confirmation showed.
|
||||
|
||||
## Checks
|
||||
|
||||
- [ ] No personal data in the change: no CV, name, e-mail address, phone number, sign-in or key. Fixtures are invented.
|
||||
- [ ] Nothing here lets the model send a form or an e-mail without the person's yes, or weakens a rule in CONTRIBUTING.md.
|
||||
@@ -0,0 +1,7 @@
|
||||
# Changelog
|
||||
|
||||
## 1.0.0 - 2026-11-04
|
||||
|
||||
The first version of AIHawk in this repository. Give it a resume (PDF, Word or text) and it writes a search plan from it, lists the matching jobs from the published job database, scores the best ones against the CV through OrcaRouter, and fills their application forms in a visible browser. It sends nothing until you select the jobs and press `a` and Enter. Sites are covered by plugins: Markdown files in `src/aihawk/plugins/`, described in [docs/plugins.md](docs/plugins.md).
|
||||
|
||||
This is a different program from the earlier AIHawk and from the Dots project that this repository held before. Their files are not read by this version. Dots moved to [feder-cr/invisible_dots](https://github.com/feder-cr/invisible_dots).
|
||||
@@ -0,0 +1,67 @@
|
||||
# Contributing
|
||||
|
||||
AIHawk is a command-line program in Python. The most useful contribution is a plugin for a job site: a Markdown file that tells the model how that site's application form works. Write one by following [docs/plugins.md](docs/plugins.md).
|
||||
|
||||
## Setup
|
||||
|
||||
You need Python 3.11 or newer. CI runs 3.12.
|
||||
|
||||
```sh
|
||||
git clone https://github.com/feder-cr/Jobs_Applier_AI_Agent_AIHawk
|
||||
cd Jobs_Applier_AI_Agent_AIHawk
|
||||
python -m venv .venv
|
||||
. .venv/bin/activate # Windows: .venv\Scripts\activate
|
||||
pip install -e ".[dev]"
|
||||
aihawk --help
|
||||
```
|
||||
|
||||
The install pulls in `invisible-playwright-mcp` at its newest release from PyPI. It is not pinned, on purpose: the CI runs every day to show when a new release stops working with AIHawk.
|
||||
|
||||
Running `aihawk` for real needs an OrcaRouter key and makes model calls. The tests need neither: they use scripted fakes (`tests/fakes.py`) and make no network calls to a model.
|
||||
|
||||
By default AIHawk keeps its files in `~/.aihawk` (on Windows, `%USERPROFILE%\.aihawk`). Use `aihawk --home <folder>` to keep a test run apart from your own search.
|
||||
|
||||
## Tests
|
||||
|
||||
```sh
|
||||
pytest -m "not browser" # unit tests: no browser, no model
|
||||
pytest -m browser # the real browser on a local form
|
||||
```
|
||||
|
||||
The browser tests run the real `invisible-playwright-mcp` and its Firefox against a form served from `127.0.0.1`, not a live site. On Linux without a display, run them under `xvfb-run -a`, as CI does.
|
||||
|
||||
## Lint
|
||||
|
||||
```sh
|
||||
ruff check .
|
||||
```
|
||||
|
||||
Ruff reads its settings from `[tool.ruff]` in `pyproject.toml` (line length 160, no extra rule sets). CI runs this exact command.
|
||||
|
||||
## What CI runs
|
||||
|
||||
The workflow is `.github/workflows/tests.yml`. It runs on every push to `main`, on every pull request, and every day at 05:41 UTC.
|
||||
|
||||
- **unit**, on Ubuntu and on Windows, Python 3.12: `pip install -e ".[dev]"`, then `ruff check .`, then `pytest -m "not browser"`.
|
||||
- **browser**, on Ubuntu: installs the Firefox libraries and xvfb, then `xvfb-run -a -s "-screen 0 1920x1080x24" pytest -m browser`.
|
||||
- **gate**: passes only if both jobs pass.
|
||||
|
||||
The other workflow, `.github/workflows/jobs-database.yml`, builds the job database and publishes it as a release. It runs on a schedule and by hand, not on pull requests.
|
||||
|
||||
## Before you open a pull request
|
||||
|
||||
- Run `ruff check .` and `pytest -m "not browser"`. Run `pytest -m browser` too if your change touches `apply.py`, the browser or the forms.
|
||||
- Keep one change to one purpose. Say in the description what you tried and what you saw.
|
||||
- For a plugin, say which posting you tried it on and what the form did. See [docs/plugins.md](docs/plugins.md) for how to test one without sending anything.
|
||||
|
||||
## What not to send
|
||||
|
||||
- A plugin you have not tried on a real posting's form. A plugin is a prompt, and an untried prompt fills forms wrongly.
|
||||
- Anything that weakens what the person controls: a change that lets the model send a form or an email without the person's yes, that lets a page's text steer the model past the rules in `apply.py`, that reads mail beyond what the mail plugins allow, or that gets around a captcha or a login.
|
||||
- Real personal data in fixtures or examples: no real CVs, names, e-mail addresses, phone numbers or screenshots of someone's application. Invent the data.
|
||||
|
||||
Use the issue templates for bugs and plugin requests. For a security problem, do not open an issue: follow [SECURITY.md](SECURITY.md). Questions go to [Discussions](https://github.com/feder-cr/Jobs_Applier_AI_Agent_AIHawk/discussions).
|
||||
|
||||
## License
|
||||
|
||||
By contributing you agree that your change is released under the [MIT license](LICENSE).
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
# Security
|
||||
|
||||
## Report a problem privately
|
||||
|
||||
Do not open a public issue or a pull request for a security problem. Report it privately with GitHub's private vulnerability reporting:
|
||||
|
||||
1. Open the repository's **Security** tab.
|
||||
2. Choose **Report a vulnerability**. Direct link: https://github.com/feder-cr/Jobs_Applier_AI_Agent_AIHawk/security/advisories/new
|
||||
|
||||
Say what you found, how to reproduce it, and what you think a person could lose. Do not include a real person's CV, e-mail or sign-in, or a working API key. Replace them with invented ones.
|
||||
|
||||
No response time is promised. The report stays private until it is fixed or until you and the maintainer agree on what to publish.
|
||||
|
||||
## What matters most here
|
||||
|
||||
AIHawk reads job pages written by strangers, fills their forms, and sends applications for a person. The problems that matter most are these:
|
||||
|
||||
1. **A posting's page steering the model.** The page's text, scripts and hidden fields reach the model as the content of tool results. Anything that lets a page make the model fill something the person did not give, click a control outside the form, or visit a site the plugin does not name is a serious problem.
|
||||
2. **Sending without the person's yes.** An application or an e-mail goes out only after the person selects the jobs, presses `a` and Enter. Anything that sends one another way, or gets past the refusal in `src/aihawk/apply.py` that stops clicks and Enter from sending a form, matters most.
|
||||
3. **Reading mail.** The mail plugins (`gmail.md`, `outlook.md`) allow reading only: a code or a link from the site the person applied to, received in the last few minutes. Anything that reads, sends, deletes or opens other mail matters.
|
||||
4. **Keys and sign-ins.** See below.
|
||||
|
||||
## Where keys and sign-ins are kept
|
||||
|
||||
- The OrcaRouter key comes from the environment variable named by `api_key_env` in `aihawk.yaml` (default `ORCAROUTER_API_KEY`), or from a file named `key` in the AIHawk folder.
|
||||
- The AIHawk folder is `~/.aihawk` by default, or the folder given by `--home` or the `AIHAWK_HOME` variable. It also holds the CV, the answers, the job data, what was sent (`applied.csv` and `data/proofs/`) and the browser profile (`browser-profile/`), which keeps the person's sign-ins.
|
||||
- Do not paste a key, a browser profile or a folder from the AIHawk folder into an issue or a pull request.
|
||||
|
||||
## Known and accepted
|
||||
|
||||
- **A script can send a form.** As a last resort, the model may run a script in the page (`browser_evaluate`). The refusal in `apply.py` does not check a script, so a script can send a form without the person's yes. This is accepted on purpose: a script is the last rung, offered only when the other browser tools cannot do the job. The script is watched after each call. Report a case where a script sends something the person did not select as a vulnerability.
|
||||
- **The mail plugins read the mailbox that the browser is signed into.** That is how a code or a link is taken from the mail. It is limited by the rules in those plugins, not by the browser.
|
||||
- **A captcha or a sign-in is not bypassed.** The model reports the form as blocked, and the person deals with it.
|
||||
- **The model is a third-party service.** Everything sent to it, including the CV and what the pages say, leaves the machine. The model's output is not trusted: the person selects, and the send is checked by AIHawk.
|
||||
@@ -0,0 +1,98 @@
|
||||
# Writing a plugin
|
||||
|
||||
A plugin tells the model how one job site's application form works. It is a Markdown file: a front matter between two `---` lines, then the instructions in plain prose. The model reads the instructions when it fills a form on that site.
|
||||
|
||||
A plugin is a prompt, not code. It cannot change what AIHawk allows. The model is told not to send a form, clicks that would send it are refused, it writes nothing that the CV and the answers do not say, and it does not get past a captcha or a login. Those rules are in `src/aihawk/apply.py`, out of a plugin's reach.
|
||||
|
||||
## Where a plugin lives
|
||||
|
||||
- `src/aihawk/plugins/<name>.md`: the bundled plugins. Add yours here for a pull request.
|
||||
- `plugins/<name>.md` in your AIHawk folder: yours alone. A file with the same name as a bundled one replaces it.
|
||||
|
||||
AIHawk reads the plugins when it starts, so restart it after you change one.
|
||||
|
||||
## Front matter
|
||||
|
||||
| Field | What it does |
|
||||
|---|---|
|
||||
| `name` | The name AIHawk shows while it fills the form. Default: the file name without `.md`. |
|
||||
| `hosts` | The sites the plugin is for, as a list, such as `[jobs.lever.co]`. A posting matches when its host is one of these or ends with `.` and one of them, so `eu.jobs.lever.co` matches too. Required unless `when` is `always`. |
|
||||
| `apply_path` | Added to the posting's path to reach the form, once: `/apply` turns `https://jobs.lever.co/acme/1` into `https://jobs.lever.co/acme/1/apply`. A link that already ends with it is left as it is. The query string is kept. Without it, the posting's own link is the form's link. |
|
||||
| `when` | Only `always`. The body is then added to every form's instructions, beside the site's own plugin. Such a plugin has no `hosts` and does not make a site count as one AIHawk can apply to. |
|
||||
| `reaches` | Only in a `when: always` plugin: the sites the model may visit with it, such as `[mail.google.com]`. A site plugin's `reaches` is read and ignored. |
|
||||
|
||||
A file that does not parse (no front matter, `hosts` missing, a `when` other than `always`, empty instructions) stops AIHawk at start with a message that names the plugin. Save the file as UTF-8 without a byte-order mark: a file that starts with one is refused with "it must start with a front matter", because the first line must be `---`.
|
||||
|
||||
## The order of sections
|
||||
|
||||
The bundled plugins follow roughly this order, which is the one to use:
|
||||
|
||||
1. How the site's pages are built: one page or several, whether the form is on the posting or at `/apply`, what loads late, what to read first, what blocks the form (a closed posting, a captcha, a sign-in).
|
||||
2. The basic fields, by id or by label.
|
||||
3. The CV upload: which input to use, and what not to click (for example "Apply with LinkedIn" or "Autofill").
|
||||
4. Questions the company adds, and the fields that are lists or menus: how to choose and how to check that the choice was taken.
|
||||
5. The send control: its selector and its label.
|
||||
6. `After sending:`, last.
|
||||
|
||||
Keep each paragraph to one subject. Name selectors in backticks and button labels in quotes, as they appear on the page.
|
||||
|
||||
## How to say how the site's pages are built
|
||||
|
||||
Start with what the model will see. Say whether the form is on the same page as the posting, and whether it needs a step first. Say what to do if the form looks empty: the bundled Workable plugin says "The form loads a moment after the page: if it looks empty, read the page again." If a posting can be closed, say what a closed posting shows and what to report.
|
||||
|
||||
## How to say "After sending:"
|
||||
|
||||
The last paragraph starts with `After sending:`. It says how the site shows that the application was received, which e-mail usually follows, and the rule that decides it:
|
||||
|
||||
> After sending: read the page: if the form is still there, the application was not received.
|
||||
|
||||
No code reads this paragraph; the model does. Say what the confirmation looks like for this site, and say that the form still being on the page means nothing was received. Do not rely on one exact phrase, because the wording is set per job board.
|
||||
|
||||
The model reports one of three results when it finishes a form: `ready` (every required field is filled from the CV and the answers), `needs_input` (a question for the person) or `blocked` (the form cannot be filled, for example because of a captcha). Say in the plugin when `blocked` is the right answer.
|
||||
|
||||
## A real example
|
||||
|
||||
`src/aihawk/plugins/lever.md`, as it is in this version:
|
||||
|
||||
```markdown
|
||||
---
|
||||
name: lever
|
||||
hosts: [jobs.lever.co]
|
||||
apply_path: /apply
|
||||
---
|
||||
The form is one page, under the heading "Submit your application". A closed posting shows "Sorry, we couldn't find anything here" (a 404 page): report the form as blocked.
|
||||
|
||||
Upload the CV first. The "ATTACH RESUME/CV" link wraps the file input `#resume-upload-input` (input[name="resume"]). Lever may fill some fields from it, so read the page again afterwards and correct anything that differs from the CV.
|
||||
|
||||
Fields are named: `name`, `email`, `phone`, `location`, `org` for the current company. Questions a company adds come after them, often in `name="cards[...]"` fields. A required field has a ✱ after its label.
|
||||
|
||||
The location field, `#location-input`, suggests places as you type: pick one of the suggestions.
|
||||
|
||||
Some forms show a captcha when sending: if the page asks for one, report the form as blocked.
|
||||
|
||||
The submit control is `#btn-submit`, "Submit application".
|
||||
|
||||
After sending: the "Success!" shown after the CV upload is the upload, not the send, so read the page after submitting. Lever usually emails a confirmation with the candidate's name, the company and the posting title. If the form is still on the page, the application was not received.
|
||||
```
|
||||
|
||||
## Try a plugin without touching the repository
|
||||
|
||||
Use a folder of your own as the AIHawk folder:
|
||||
|
||||
```sh
|
||||
mkdir -p ~/aihawk-trial/plugins
|
||||
# write ~/aihawk-trial/plugins/mysite.md
|
||||
aihawk --home ~/aihawk-trial
|
||||
```
|
||||
|
||||
On Windows, use `%USERPROFILE%\aihawk-trial\plugins` and the same command. The `AIHAWK_HOME` environment variable does the same as `--home`. The trial folder gets its own key, CV and answers, so your real search is not affected. Delete the folder when you are done.
|
||||
|
||||
## Test a plugin without sending anything to an employer
|
||||
|
||||
AIHawk fills a form only after your yes, and that yes sends it. So do not test a plugin on a live posting unless you mean to apply. Test it in one of these ways:
|
||||
|
||||
- **Unit tests.** `tests/test_plugins.py` shows how a plugin is checked: `fill_form` runs against `ScriptedModel` and `ScriptedBrowser` from `tests/fakes.py`, with no network and no send. Run `pytest -m "not browser"`.
|
||||
- **A local page.** `tests/test_real_browser.py` serves a form from `127.0.0.1` and runs the real browser on it. Copy a site's form structure into such a page to check the selectors, with `pytest -m browser`.
|
||||
- **The batch screen.** If you select a job and press `a` by mistake, Esc at the confirmation sends nothing and says "Nothing was sent."
|
||||
|
||||
When you test on a real posting on purpose, say which one in the pull request, and what the form did, including whether the confirmation page showed.
|
||||
Reference in new issue
Block a user