aihawk 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- aihawk-0.1.0/.github/CODE_OF_CONDUCT.md +33 -0
- aihawk-0.1.0/.github/CONTRIBUTING.md +106 -0
- aihawk-0.1.0/.github/FUNDING.yml +1 -0
- aihawk-0.1.0/.github/ISSUE_TEMPLATE/bug-issue.yml +90 -0
- aihawk-0.1.0/.github/ISSUE_TEMPLATE/config.yml +8 -0
- aihawk-0.1.0/.github/ISSUE_TEMPLATE/documentation-issue.yml +39 -0
- aihawk-0.1.0/.github/ISSUE_TEMPLATE/enhancement-issue.yml +46 -0
- aihawk-0.1.0/.github/workflows/ci.yml +69 -0
- aihawk-0.1.0/.github/workflows/publish.yml +109 -0
- aihawk-0.1.0/.github/workflows/stale.yml +42 -0
- aihawk-0.1.0/.gitignore +167 -0
- aihawk-0.1.0/LICENSE +21 -0
- aihawk-0.1.0/PKG-INFO +177 -0
- aihawk-0.1.0/README.md +159 -0
- aihawk-0.1.0/articles/README.md +45 -0
- aihawk-0.1.0/assets/AIHawk.png +0 -0
- aihawk-0.1.0/assets/aihawk-logo-dark.png +0 -0
- aihawk-0.1.0/assets/aihawk-logo-light.png +0 -0
- aihawk-0.1.0/assets/invisible-playwright-banner.png +0 -0
- aihawk-0.1.0/assets/laboro.png +0 -0
- aihawk-0.1.0/pyproject.toml +52 -0
- aihawk-0.1.0/scripts/check_english_only.py +365 -0
- aihawk-0.1.0/src/aihawk/__init__.py +2 -0
- aihawk-0.1.0/src/aihawk/__main__.py +4 -0
- aihawk-0.1.0/src/aihawk/actions_help.py +53 -0
- aihawk-0.1.0/src/aihawk/agent.py +156 -0
- aihawk-0.1.0/src/aihawk/brain.py +115 -0
- aihawk-0.1.0/src/aihawk/cli.py +120 -0
- aihawk-0.1.0/src/aihawk/link.py +131 -0
- aihawk-0.1.0/src/aihawk/llm.py +25 -0
- aihawk-0.1.0/src/aihawk/runner.py +57 -0
- aihawk-0.1.0/src/aihawk/web.py +801 -0
- aihawk-0.1.0/tests/test_agent.py +81 -0
- aihawk-0.1.0/tests/test_agent_loop.py +843 -0
- aihawk-0.1.0/tests/test_cli.py +24 -0
- aihawk-0.1.0/tests/test_cli_surface.py +517 -0
- aihawk-0.1.0/tests/test_import.py +3 -0
- aihawk-0.1.0/tests/test_key_isolation.py +385 -0
- aihawk-0.1.0/tests/test_llm.py +15 -0
- aihawk-0.1.0/tests/test_openrouter_only.py +392 -0
- aihawk-0.1.0/tests/test_real_cli.py +19 -0
- aihawk-0.1.0/tests/test_runner.py +27 -0
- aihawk-0.1.0/tests/test_ui_drive.py +874 -0
- aihawk-0.1.0/tests/test_web_service.py +302 -0
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Code of Conduct
|
|
2
|
+
|
|
3
|
+
This project follows the [Contributor Covenant, v2.1](https://www.contributor-covenant.org/version/2/1/code_of_conduct/).
|
|
4
|
+
|
|
5
|
+
## Our Pledge
|
|
6
|
+
|
|
7
|
+
We pledge to make participation in our community a harassment-free experience for everyone.
|
|
8
|
+
|
|
9
|
+
## Standards
|
|
10
|
+
|
|
11
|
+
Examples of behavior that contributes to a positive environment:
|
|
12
|
+
|
|
13
|
+
- Using welcoming and inclusive language
|
|
14
|
+
- Being respectful of differing viewpoints and experiences
|
|
15
|
+
- Gracefully accepting constructive criticism
|
|
16
|
+
- Focusing on what is best for the community
|
|
17
|
+
|
|
18
|
+
Examples of unacceptable behavior:
|
|
19
|
+
|
|
20
|
+
- The use of sexualized language or imagery
|
|
21
|
+
- Trolling, insulting or derogatory comments, and personal or political attacks
|
|
22
|
+
- Public or private harassment
|
|
23
|
+
- Publishing others' private information without explicit permission
|
|
24
|
+
|
|
25
|
+
## Enforcement
|
|
26
|
+
|
|
27
|
+
Instances of unacceptable behavior may be reported by contacting the maintainer at **federico.elia.majo@gmail.com**. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances.
|
|
28
|
+
|
|
29
|
+
The maintainer is obligated to maintain confidentiality with regard to the reporter of an incident.
|
|
30
|
+
|
|
31
|
+
## Attribution
|
|
32
|
+
|
|
33
|
+
This Code of Conduct is adapted from the [Contributor Covenant](https://www.contributor-covenant.org), version 2.1, available at https://www.contributor-covenant.org/version/2/1/code_of_conduct.html.
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# Contributing to AIHawk
|
|
2
|
+
|
|
3
|
+
Thanks for being here. This page says what this repository is, so you can tell in
|
|
4
|
+
one minute whether your change belongs here or in one of the packages below.
|
|
5
|
+
|
|
6
|
+
## What lives here
|
|
7
|
+
|
|
8
|
+
The `aihawk` package: the two-pane interface, the loop that turns a sentence into
|
|
9
|
+
browser actions, and the command line around both. If you are changing what a
|
|
10
|
+
person sees, or how the model decides what to do next, it is here.
|
|
11
|
+
|
|
12
|
+
What is NOT here is the browser. AIHawk does not drive Firefox directly - it
|
|
13
|
+
talks to an MCP server over MCP, using the same fourteen tools any other client
|
|
14
|
+
gets. That is deliberate and it is the thing to understand before changing
|
|
15
|
+
anything: this interface has no privileged path to the page, so a browser
|
|
16
|
+
capability it needs is a capability every client gets, or it does not exist.
|
|
17
|
+
|
|
18
|
+
This page said the opposite earlier on 2 September 2026, when the repository held
|
|
19
|
+
only documentation. The package moved in the same day.
|
|
20
|
+
|
|
21
|
+
## Where the code is
|
|
22
|
+
|
|
23
|
+
| Repository | What it holds |
|
|
24
|
+
|---|---|
|
|
25
|
+
| **this one** | the interface, the agent loop, the CLI |
|
|
26
|
+
| [invisible-playwright-mcp](https://github.com/feder-cr/invisible-playwright-mcp) | the MCP server: the fourteen tools, and nothing with a face |
|
|
27
|
+
| [invisible_playwright](https://github.com/feder-cr/invisible_playwright) | the Python wrapper, the launcher, and the patched browser it pins |
|
|
28
|
+
| [invisible_core](https://github.com/feder-cr/invisible_core) | seed to fingerprint to preferences, proxy and geolocation |
|
|
29
|
+
|
|
30
|
+
Where things go:
|
|
31
|
+
|
|
32
|
+
- the page, the conversation, the step list, the model's behaviour: **here**
|
|
33
|
+
- a tool that returns the wrong thing, or a click that does not land: **the MCP server**
|
|
34
|
+
- the browser failing to start, a proxy not used, a timezone that does not match
|
|
35
|
+
the exit country: **the wrapper or the core**
|
|
36
|
+
- a detector spotting the browser: almost always the engine, not this repository
|
|
37
|
+
|
|
38
|
+
If you are not sure, open the issue here and it will be moved.
|
|
39
|
+
|
|
40
|
+
## Development setup
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
git clone https://github.com/feder-cr/AIHawk.git
|
|
44
|
+
cd AIHawk
|
|
45
|
+
python -m venv .venv
|
|
46
|
+
source .venv/bin/activate # Windows: .venv\Scripts\activate
|
|
47
|
+
pip install -e ".[test]"
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Python 3.11 or newer, Windows or Linux. No key is needed to work on it: `aihawk
|
|
51
|
+
ui` without one runs the literal-command placeholder, which drives the real
|
|
52
|
+
browser through the real server and is how most of this was tested.
|
|
53
|
+
|
|
54
|
+
## Running the tests
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
pytest # the default selection, no browser, seconds
|
|
58
|
+
pytest -m ui # drives a REAL browser through a REAL server
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
The `ui` tests are deselected by default because they are slow and they must run
|
|
62
|
+
serially: they launch a browser, and two browser benches on one machine produce
|
|
63
|
+
results that are noise with numbers on them. Run them one at a time, on a machine
|
|
64
|
+
that is not doing anything else.
|
|
65
|
+
|
|
66
|
+
They assert what happened INSIDE the page, never what a tool said about itself. A
|
|
67
|
+
tool that answers "clicked #go" while nothing moved is the failure they exist to
|
|
68
|
+
catch, so the tool's own success string is never the assertion.
|
|
69
|
+
|
|
70
|
+
## Pull requests
|
|
71
|
+
|
|
72
|
+
Fork, branch, open the pull request against `main`. There is one branch and no
|
|
73
|
+
release train.
|
|
74
|
+
|
|
75
|
+
Keep a pull request to one change, and say in the description what a reader would
|
|
76
|
+
be able to do afterwards that they could not do before. If you change what the
|
|
77
|
+
interface shows, include a screenshot: this project has already had the case
|
|
78
|
+
where five green tests passed while the pane showed a black rectangle, and the
|
|
79
|
+
picture was the only thing that noticed.
|
|
80
|
+
|
|
81
|
+
## Opening an issue
|
|
82
|
+
|
|
83
|
+
Use one of the [templates](https://github.com/feder-cr/AIHawk/issues/new/choose).
|
|
84
|
+
Before you do, search the open and closed issues.
|
|
85
|
+
|
|
86
|
+
For a bug, the two things that decide how fast it can be answered are the exact
|
|
87
|
+
commands you ran and what happened instead of what you expected. Include your OS
|
|
88
|
+
and Python version, and whether you were running `ui` or `do`.
|
|
89
|
+
|
|
90
|
+
Please do not report a security problem in a public issue. In particular, never
|
|
91
|
+
paste an OpenRouter key into one: if you have, rotate it before anything else.
|
|
92
|
+
|
|
93
|
+
## Tone
|
|
94
|
+
|
|
95
|
+
Questions are welcome and beginners are welcome. If something in the README was
|
|
96
|
+
not clear enough for you, that is worth an issue on its own, because it was
|
|
97
|
+
probably not clear to somebody else either.
|
|
98
|
+
|
|
99
|
+
## Licence
|
|
100
|
+
|
|
101
|
+
By contributing you agree that your contribution is licensed under the MIT
|
|
102
|
+
licence, which is the licence in [LICENSE](../LICENSE). Everything received
|
|
103
|
+
before 2 September 2026 was contributed under AGPL-3.0 and stays under it: a
|
|
104
|
+
licence already granted is not withdrawn by changing that file.
|
|
105
|
+
|
|
106
|
+
Participation is covered by the [Code of Conduct](CODE_OF_CONDUCT.md).
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
github: feder-cr
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
name: Bug report
|
|
2
|
+
description: Report a bug or an issue that isn't working as expected.
|
|
3
|
+
title: "[BUG]: <Provide a clear, descriptive title>"
|
|
4
|
+
labels: ["bug"]
|
|
5
|
+
assignees: []
|
|
6
|
+
|
|
7
|
+
body:
|
|
8
|
+
- type: markdown
|
|
9
|
+
attributes:
|
|
10
|
+
value: |
|
|
11
|
+
Please fill out the following information to help us resolve the issue.
|
|
12
|
+
|
|
13
|
+
- type: input
|
|
14
|
+
id: description
|
|
15
|
+
attributes:
|
|
16
|
+
label: Describe the bug
|
|
17
|
+
description: A clear and concise description of what the bug is.
|
|
18
|
+
placeholder: "Describe the bug in detail..."
|
|
19
|
+
|
|
20
|
+
- type: textarea
|
|
21
|
+
id: steps
|
|
22
|
+
attributes:
|
|
23
|
+
label: Steps to reproduce
|
|
24
|
+
description: |
|
|
25
|
+
Steps to reproduce the behavior:
|
|
26
|
+
1. Use branch named '...'
|
|
27
|
+
2. Go to file '...'
|
|
28
|
+
3. Find property named '...'
|
|
29
|
+
4. Change '...'
|
|
30
|
+
5. Run program using command '...'
|
|
31
|
+
6. See error
|
|
32
|
+
placeholder: "List the steps to reproduce the bug..."
|
|
33
|
+
|
|
34
|
+
- type: input
|
|
35
|
+
id: expected
|
|
36
|
+
attributes:
|
|
37
|
+
label: Expected behavior
|
|
38
|
+
description: What you expected to happen.
|
|
39
|
+
placeholder: "What was the expected result?"
|
|
40
|
+
|
|
41
|
+
- type: input
|
|
42
|
+
id: actual
|
|
43
|
+
attributes:
|
|
44
|
+
label: Actual behavior
|
|
45
|
+
description: What actually happened instead.
|
|
46
|
+
placeholder: "What happened instead?"
|
|
47
|
+
|
|
48
|
+
- type: dropdown
|
|
49
|
+
id: branch
|
|
50
|
+
attributes:
|
|
51
|
+
label: Branch
|
|
52
|
+
description: Specify the branch you were using when the bug occurred.
|
|
53
|
+
options:
|
|
54
|
+
- main
|
|
55
|
+
- other
|
|
56
|
+
|
|
57
|
+
- type: input
|
|
58
|
+
id: otherBranch
|
|
59
|
+
attributes:
|
|
60
|
+
label: Branch name
|
|
61
|
+
description: If you selected ```other``` branch for the previous question, what is the branch name?
|
|
62
|
+
placeholder: "what-is-the-name-of-the-branch-you-were-using"
|
|
63
|
+
|
|
64
|
+
- type: input
|
|
65
|
+
id: pythonVersion
|
|
66
|
+
attributes:
|
|
67
|
+
label: Python version
|
|
68
|
+
description: Specify the version of Python you were using when the bug occurred.
|
|
69
|
+
placeholder: "e.g., 3.12.5(64b)"
|
|
70
|
+
|
|
71
|
+
- type: input
|
|
72
|
+
id: llm
|
|
73
|
+
attributes:
|
|
74
|
+
label: LLM Used
|
|
75
|
+
description: Specify the LLM provider you were using when the bug occurred.
|
|
76
|
+
placeholder: "e.g., ChatGPT"
|
|
77
|
+
|
|
78
|
+
- type: input
|
|
79
|
+
id: model
|
|
80
|
+
attributes:
|
|
81
|
+
label: Model used
|
|
82
|
+
description: Specify the LLM model you were using when the bug occurred.
|
|
83
|
+
placeholder: "e.g., GPT-4o-mini"
|
|
84
|
+
|
|
85
|
+
- type: textarea
|
|
86
|
+
id: additional
|
|
87
|
+
attributes:
|
|
88
|
+
label: Additional context
|
|
89
|
+
description: Add any other context about the problem here.
|
|
90
|
+
placeholder: "Any additional information..."
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
blank_issues_enabled: true
|
|
2
|
+
contact_links:
|
|
3
|
+
- name: Questions
|
|
4
|
+
url: https://t.me/AIhawkCommunity
|
|
5
|
+
about: You can join the discussions on Telegram.
|
|
6
|
+
- name: Contributing guide
|
|
7
|
+
url: https://github.com/feder-cr/AIHawk/blob/main/.github/CONTRIBUTING.md
|
|
8
|
+
about: Which repository your issue belongs in, and what to include.
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
name: Documentation request
|
|
2
|
+
description: Suggest improvements or additions to the project's documentation.
|
|
3
|
+
title: "[DOCS]: <Provide a short title>"
|
|
4
|
+
labels: ["documentation"]
|
|
5
|
+
assignees: []
|
|
6
|
+
|
|
7
|
+
body:
|
|
8
|
+
- type: markdown
|
|
9
|
+
attributes:
|
|
10
|
+
value: |
|
|
11
|
+
Thanks for helping to improve the project's documentation! Please provide the following details to ensure your request is clear.
|
|
12
|
+
|
|
13
|
+
- type: input
|
|
14
|
+
id: doc_section
|
|
15
|
+
attributes:
|
|
16
|
+
label: Affected documentation section
|
|
17
|
+
description: Specify which part of the documentation needs improvement or addition.
|
|
18
|
+
placeholder: "e.g., Installation Guide, API Reference..."
|
|
19
|
+
|
|
20
|
+
- type: textarea
|
|
21
|
+
id: description
|
|
22
|
+
attributes:
|
|
23
|
+
label: Documentation improvement description
|
|
24
|
+
description: Describe the specific improvements or additions you suggest.
|
|
25
|
+
placeholder: "Explain what changes you propose and why..."
|
|
26
|
+
|
|
27
|
+
- type: input
|
|
28
|
+
id: reason
|
|
29
|
+
attributes:
|
|
30
|
+
label: Why is this change necessary?
|
|
31
|
+
description: Explain why the documentation needs to be updated or expanded.
|
|
32
|
+
placeholder: "Describe the issue or gap in the documentation..."
|
|
33
|
+
|
|
34
|
+
- type: input
|
|
35
|
+
id: additional
|
|
36
|
+
attributes:
|
|
37
|
+
label: Additional context
|
|
38
|
+
description: Add any other context, such as related documentation, external resources, or screenshots.
|
|
39
|
+
placeholder: "Add any other supporting information..."
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
name: Feature request
|
|
2
|
+
description: Suggest a new feature or improvement for the project.
|
|
3
|
+
title: "[FEATURE]: <Provide a descriptive title>"
|
|
4
|
+
labels: ["enhancement"]
|
|
5
|
+
assignees: []
|
|
6
|
+
|
|
7
|
+
body:
|
|
8
|
+
- type: markdown
|
|
9
|
+
attributes:
|
|
10
|
+
value: |
|
|
11
|
+
Thank you for suggesting a feature! Please fill out the form below to help us understand your idea.
|
|
12
|
+
|
|
13
|
+
- type: input
|
|
14
|
+
id: summary
|
|
15
|
+
attributes:
|
|
16
|
+
label: Feature summary
|
|
17
|
+
description: Provide a short summary of the feature you're requesting.
|
|
18
|
+
placeholder: "Summarize the feature in a few words..."
|
|
19
|
+
|
|
20
|
+
- type: textarea
|
|
21
|
+
id: description
|
|
22
|
+
attributes:
|
|
23
|
+
label: Feature description
|
|
24
|
+
description: A detailed description of the feature or improvement.
|
|
25
|
+
placeholder: "Describe the feature in detail..."
|
|
26
|
+
|
|
27
|
+
- type: input
|
|
28
|
+
id: motivation
|
|
29
|
+
attributes:
|
|
30
|
+
label: Motivation
|
|
31
|
+
description: Explain why this feature would be beneficial and how it solves a problem.
|
|
32
|
+
placeholder: "Why do you need this feature?"
|
|
33
|
+
|
|
34
|
+
- type: textarea
|
|
35
|
+
id: alternatives
|
|
36
|
+
attributes:
|
|
37
|
+
label: Alternatives considered
|
|
38
|
+
description: List any alternative solutions or features you've considered.
|
|
39
|
+
placeholder: "Are there any alternative features or solutions you’ve considered?"
|
|
40
|
+
|
|
41
|
+
- type: input
|
|
42
|
+
id: additional
|
|
43
|
+
attributes:
|
|
44
|
+
label: Additional context
|
|
45
|
+
description: Add any other context or screenshots to support your feature request.
|
|
46
|
+
placeholder: "Any additional information..."
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
name: tests
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
contents: read
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
# This repository is public and everything in it is English by rule. The gate
|
|
13
|
+
# is a COPY and not a reference to the one in the sibling packages, on purpose:
|
|
14
|
+
# it scans the tree it is run from, and running a sibling's copy from here
|
|
15
|
+
# silently scans the wrong repository and reports clean. That happened once
|
|
16
|
+
# already, which is why each package carries its own.
|
|
17
|
+
english:
|
|
18
|
+
runs-on: ubuntu-latest
|
|
19
|
+
steps:
|
|
20
|
+
- uses: actions/checkout@v4
|
|
21
|
+
- uses: actions/setup-python@v5
|
|
22
|
+
with:
|
|
23
|
+
python-version: "3.12"
|
|
24
|
+
- name: the repository is English
|
|
25
|
+
run: |
|
|
26
|
+
python scripts/check_english_only.py --selftest
|
|
27
|
+
python scripts/check_english_only.py
|
|
28
|
+
|
|
29
|
+
test:
|
|
30
|
+
runs-on: ${{ matrix.os }}
|
|
31
|
+
strategy:
|
|
32
|
+
fail-fast: false
|
|
33
|
+
matrix:
|
|
34
|
+
os: [ubuntu-latest, windows-latest]
|
|
35
|
+
python-version: ["3.11", "3.12", "3.13"]
|
|
36
|
+
steps:
|
|
37
|
+
- uses: actions/checkout@v4
|
|
38
|
+
- uses: actions/setup-python@v5
|
|
39
|
+
with:
|
|
40
|
+
python-version: ${{ matrix.python-version }}
|
|
41
|
+
- name: install and test
|
|
42
|
+
# The default selection excludes `ui`, which drives a real browser: those
|
|
43
|
+
# need an engine download and a machine doing nothing else, and a runner
|
|
44
|
+
# is neither. They are run by hand, serially, before a release.
|
|
45
|
+
run: |
|
|
46
|
+
python -m pip install --upgrade pip
|
|
47
|
+
pip install -e ".[test]"
|
|
48
|
+
python -m pytest -q
|
|
49
|
+
|
|
50
|
+
# This package is published, so the thing that must work is the WHEEL, not the
|
|
51
|
+
# checkout. An editable install can pass every test while the built artifact is
|
|
52
|
+
# missing a module: the tests import from src/ either way, and only a clean
|
|
53
|
+
# install from the wheel can tell the difference.
|
|
54
|
+
build:
|
|
55
|
+
runs-on: ubuntu-latest
|
|
56
|
+
steps:
|
|
57
|
+
- uses: actions/checkout@v4
|
|
58
|
+
- uses: actions/setup-python@v5
|
|
59
|
+
with:
|
|
60
|
+
python-version: "3.12"
|
|
61
|
+
- name: the wheel builds, installs clean, and answers
|
|
62
|
+
run: |
|
|
63
|
+
python -m pip install --upgrade pip
|
|
64
|
+
pip install build
|
|
65
|
+
python -m build --wheel
|
|
66
|
+
python -m venv /tmp/clean
|
|
67
|
+
/tmp/clean/bin/pip install dist/*.whl
|
|
68
|
+
/tmp/clean/bin/aihawk --help
|
|
69
|
+
/tmp/clean/bin/python -c "import aihawk.web, aihawk.brain, aihawk.link, aihawk.cli"
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
name: publish
|
|
2
|
+
|
|
3
|
+
# Publish `aihawk` from a tag via PyPI Trusted Publishing (OIDC), with no
|
|
4
|
+
# long-lived token anywhere. The trust lives in the `pypi` GitHub Environment,
|
|
5
|
+
# minted per run and valid for minutes. Only the `upload` job names that
|
|
6
|
+
# environment, and it declares `needs: gate`, so a red gate means the only job
|
|
7
|
+
# that can authenticate never starts.
|
|
8
|
+
#
|
|
9
|
+
# BEFORE THE FIRST RUN, on PyPI: the name is unregistered, so Your projects will
|
|
10
|
+
# not list it. Use Account -> Publishing -> Add a pending publisher with:
|
|
11
|
+
# PyPI Project Name: aihawk
|
|
12
|
+
# Owner: feder-cr
|
|
13
|
+
# Repository name: AIHawk
|
|
14
|
+
# Workflow name: publish.yml
|
|
15
|
+
# Environment name: pypi
|
|
16
|
+
# Until that exists the upload fails with `invalid-publisher`, which is the
|
|
17
|
+
# correct failure: nothing has said this workflow may speak for this project.
|
|
18
|
+
|
|
19
|
+
on:
|
|
20
|
+
push:
|
|
21
|
+
tags:
|
|
22
|
+
- "v*"
|
|
23
|
+
workflow_dispatch:
|
|
24
|
+
|
|
25
|
+
permissions:
|
|
26
|
+
contents: read
|
|
27
|
+
|
|
28
|
+
jobs:
|
|
29
|
+
already-published:
|
|
30
|
+
runs-on: ubuntu-latest
|
|
31
|
+
outputs:
|
|
32
|
+
present: ${{ steps.probe.outputs.present }}
|
|
33
|
+
version: ${{ steps.probe.outputs.version }}
|
|
34
|
+
steps:
|
|
35
|
+
- uses: actions/checkout@v4
|
|
36
|
+
- id: probe
|
|
37
|
+
shell: bash
|
|
38
|
+
# Asks the INDEX, not the ledger and not the tag: a re-pushed tag or a
|
|
39
|
+
# backfill is then a logged no-op rather than a red run, and the only
|
|
40
|
+
# answer that stops everything is one that is neither yes nor no.
|
|
41
|
+
run: |
|
|
42
|
+
VERSION="$(grep -m1 '^version = ' pyproject.toml | cut -d'"' -f2)"
|
|
43
|
+
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
|
|
44
|
+
CODE=$(curl -s -o /dev/null -w '%{http_code}' "https://pypi.org/pypi/aihawk/$VERSION/json")
|
|
45
|
+
case "$CODE" in
|
|
46
|
+
200) echo "present=yes" >> "$GITHUB_OUTPUT"
|
|
47
|
+
echo "::notice::aihawk $VERSION is already on the index. No-op, not a failure." ;;
|
|
48
|
+
404) echo "present=no" >> "$GITHUB_OUTPUT" ;;
|
|
49
|
+
*) echo "the index answered $CODE for $VERSION, which is neither present nor absent. Refusing rather than guessing." >&2
|
|
50
|
+
exit 1 ;;
|
|
51
|
+
esac
|
|
52
|
+
|
|
53
|
+
gate:
|
|
54
|
+
needs: already-published
|
|
55
|
+
if: needs.already-published.outputs.present != 'yes'
|
|
56
|
+
runs-on: ubuntu-latest
|
|
57
|
+
steps:
|
|
58
|
+
- uses: actions/checkout@v4
|
|
59
|
+
- uses: actions/setup-python@v5
|
|
60
|
+
with:
|
|
61
|
+
python-version: "3.12"
|
|
62
|
+
- name: the tag names the version being built
|
|
63
|
+
if: github.event_name == 'push'
|
|
64
|
+
shell: bash
|
|
65
|
+
run: |
|
|
66
|
+
TAG="${GITHUB_REF_NAME#v}"
|
|
67
|
+
HAVE="${{ needs.already-published.outputs.version }}"
|
|
68
|
+
if [ "$TAG" != "$HAVE" ]; then
|
|
69
|
+
echo "tag v$TAG does not name the version in pyproject.toml ($HAVE)" >&2
|
|
70
|
+
exit 1
|
|
71
|
+
fi
|
|
72
|
+
- name: install and run the suite
|
|
73
|
+
# A tag push does not trigger the ordinary test workflow, which runs on
|
|
74
|
+
# branches and pull requests, so without this a tag could publish code
|
|
75
|
+
# that no job has ever run.
|
|
76
|
+
run: |
|
|
77
|
+
python -m pip install --upgrade pip
|
|
78
|
+
pip install -e ".[test]"
|
|
79
|
+
python -m pytest -q
|
|
80
|
+
- name: the wheel installs into an empty environment and answers
|
|
81
|
+
# The published artifact is a wheel, and the suite above imports from
|
|
82
|
+
# src/ either way. Only a clean install can tell you that the thing
|
|
83
|
+
# about to reach users actually starts.
|
|
84
|
+
run: |
|
|
85
|
+
pip install build
|
|
86
|
+
python -m build --wheel
|
|
87
|
+
python -m venv /tmp/clean
|
|
88
|
+
/tmp/clean/bin/pip install dist/*.whl
|
|
89
|
+
/tmp/clean/bin/aihawk --help
|
|
90
|
+
|
|
91
|
+
upload:
|
|
92
|
+
needs: [already-published, gate]
|
|
93
|
+
if: needs.already-published.outputs.present != 'yes'
|
|
94
|
+
runs-on: ubuntu-latest
|
|
95
|
+
environment: pypi # the OIDC trust relationship lives HERE, behind the gate
|
|
96
|
+
permissions:
|
|
97
|
+
id-token: write # trusted publishing; no token in a secret anywhere
|
|
98
|
+
steps:
|
|
99
|
+
- uses: actions/checkout@v4
|
|
100
|
+
- uses: actions/setup-python@v5
|
|
101
|
+
with:
|
|
102
|
+
python-version: "3.12"
|
|
103
|
+
- run: python -m pip install --upgrade pip build
|
|
104
|
+
- run: python -m build
|
|
105
|
+
- name: twine check
|
|
106
|
+
run: |
|
|
107
|
+
pip install twine
|
|
108
|
+
twine check dist/*
|
|
109
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
name: Mark and Close Stale Issues
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
# Schedule the workflow to run periodically (e.g., daily at 1:30 AM UTC)
|
|
5
|
+
schedule:
|
|
6
|
+
- cron: "30 1 * * *"
|
|
7
|
+
workflow_dispatch:
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
stale:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
permissions:
|
|
13
|
+
issues: write
|
|
14
|
+
pull-requests: write
|
|
15
|
+
|
|
16
|
+
steps:
|
|
17
|
+
- name: Run Stale Action
|
|
18
|
+
uses: actions/stale@v9
|
|
19
|
+
with:
|
|
20
|
+
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
|
21
|
+
days-before-issue-stale: 10 # Days of inactivity before marking an issue as stale
|
|
22
|
+
days-before-issue-close: 5 # Days after being marked stale before closing the issue
|
|
23
|
+
stale-issue-label: "stale" # Label to apply to stale issues
|
|
24
|
+
exempt-issue-labels: "pinned,important" # Labels to exclude from being marked as stale
|
|
25
|
+
exempt-issue-assignees: true # Exempt issues with assignees from being marked as stale
|
|
26
|
+
stale-issue-message: "This issue has been marked as stale due to inactivity. Please comment or update if this is still relevant."
|
|
27
|
+
close-issue-message: "This issue was closed due to prolonged inactivity."
|
|
28
|
+
days-before-pr-stale: 10 # Days of inactivity before marking a PR as stale
|
|
29
|
+
days-before-pr-close: 2 # Days after being marked stale before closing the PR
|
|
30
|
+
stale-pr-label: "stale" # Label to apply to stale PRs
|
|
31
|
+
exempt-pr-labels: "pinned,important" # Labels to exclude from being marked as stale
|
|
32
|
+
stale-pr-message: >
|
|
33
|
+
"This pull request has been marked as stale due to inactivity.
|
|
34
|
+
To keep it open, you can:
|
|
35
|
+
- Show progress by updating the PR with new commits.
|
|
36
|
+
- Continue the conversation by adding comments or requesting clarification on any blockers.
|
|
37
|
+
- Resolve pending feedback by replying to unresolved comments or implementing suggested changes.
|
|
38
|
+
- Indicate readiness for review by explicitly requesting a review from maintainers or reviewers.
|
|
39
|
+
If no action is taken within 7 days, this pull request will be closed."
|
|
40
|
+
close-pr-message: "This PR was closed due to prolonged inactivity."
|
|
41
|
+
remove-stale-when-updated: true # Remove the stale label if there is new activity
|
|
42
|
+
operations-per-run: 20 # Number of issues to process per run (default is 30)
|