atsfill 0.0.4.dev89__tar.gz → 0.0.4.dev90__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.
- {atsfill-0.0.4.dev89 → atsfill-0.0.4.dev90}/PKG-INFO +3 -3
- {atsfill-0.0.4.dev89 → atsfill-0.0.4.dev90}/README.md +2 -2
- {atsfill-0.0.4.dev89 → atsfill-0.0.4.dev90}/pyproject.toml +1 -1
- {atsfill-0.0.4.dev89 → atsfill-0.0.4.dev90}/pyproject.toml.orig +1 -1
- {atsfill-0.0.4.dev89 → atsfill-0.0.4.dev90}/src/atsfill/adapters/phenom.py +1 -1
- {atsfill-0.0.4.dev89 → atsfill-0.0.4.dev90}/src/atsfill/adapters/workday.py +1 -1
- {atsfill-0.0.4.dev89 → atsfill-0.0.4.dev90}/src/atsfill/adapters/wttj.py +4 -4
- {atsfill-0.0.4.dev89 → atsfill-0.0.4.dev90}/src/atsfill/core/browser.py +1 -1
- {atsfill-0.0.4.dev89 → atsfill-0.0.4.dev90}/src/atsfill/core/results.py +1 -1
- {atsfill-0.0.4.dev89 → atsfill-0.0.4.dev90}/src/atsfill/registry.toml +2 -2
- {atsfill-0.0.4.dev89 → atsfill-0.0.4.dev90}/LICENSE +0 -0
- {atsfill-0.0.4.dev89 → atsfill-0.0.4.dev90}/src/atsfill/__init__.py +0 -0
- {atsfill-0.0.4.dev89 → atsfill-0.0.4.dev90}/src/atsfill/adapters/__init__.py +0 -0
- {atsfill-0.0.4.dev89 → atsfill-0.0.4.dev90}/src/atsfill/adapters/ashby.py +0 -0
- {atsfill-0.0.4.dev89 → atsfill-0.0.4.dev90}/src/atsfill/adapters/bamboohr.py +0 -0
- {atsfill-0.0.4.dev89 → atsfill-0.0.4.dev90}/src/atsfill/adapters/breezy.py +0 -0
- {atsfill-0.0.4.dev89 → atsfill-0.0.4.dev90}/src/atsfill/adapters/dayforce.py +0 -0
- {atsfill-0.0.4.dev89 → atsfill-0.0.4.dev90}/src/atsfill/adapters/eightfold.py +0 -0
- {atsfill-0.0.4.dev89 → atsfill-0.0.4.dev90}/src/atsfill/adapters/greenhouse.py +0 -0
- {atsfill-0.0.4.dev89 → atsfill-0.0.4.dev90}/src/atsfill/adapters/hireology.py +0 -0
- {atsfill-0.0.4.dev89 → atsfill-0.0.4.dev90}/src/atsfill/adapters/hiringthing.py +0 -0
- {atsfill-0.0.4.dev89 → atsfill-0.0.4.dev90}/src/atsfill/adapters/jazzhr.py +0 -0
- {atsfill-0.0.4.dev89 → atsfill-0.0.4.dev90}/src/atsfill/adapters/lever.py +0 -0
- {atsfill-0.0.4.dev89 → atsfill-0.0.4.dev90}/src/atsfill/adapters/manatal.py +0 -0
- {atsfill-0.0.4.dev89 → atsfill-0.0.4.dev90}/src/atsfill/adapters/oracle.py +0 -0
- {atsfill-0.0.4.dev89 → atsfill-0.0.4.dev90}/src/atsfill/adapters/paycor.py +0 -0
- {atsfill-0.0.4.dev89 → atsfill-0.0.4.dev90}/src/atsfill/adapters/paylocity.py +0 -0
- {atsfill-0.0.4.dev89 → atsfill-0.0.4.dev90}/src/atsfill/adapters/personio.py +0 -0
- {atsfill-0.0.4.dev89 → atsfill-0.0.4.dev90}/src/atsfill/adapters/pinpoint.py +0 -0
- {atsfill-0.0.4.dev89 → atsfill-0.0.4.dev90}/src/atsfill/adapters/recruitee.py +0 -0
- {atsfill-0.0.4.dev89 → atsfill-0.0.4.dev90}/src/atsfill/adapters/rippling.py +0 -0
- {atsfill-0.0.4.dev89 → atsfill-0.0.4.dev90}/src/atsfill/adapters/smartrecruiters.py +0 -0
- {atsfill-0.0.4.dev89 → atsfill-0.0.4.dev90}/src/atsfill/adapters/teamtailor.py +0 -0
- {atsfill-0.0.4.dev89 → atsfill-0.0.4.dev90}/src/atsfill/adapters/workable.py +0 -0
- {atsfill-0.0.4.dev89 → atsfill-0.0.4.dev90}/src/atsfill/adapters/zohorecruit.py +0 -0
- {atsfill-0.0.4.dev89 → atsfill-0.0.4.dev90}/src/atsfill/cli.py +0 -0
- {atsfill-0.0.4.dev89 → atsfill-0.0.4.dev90}/src/atsfill/core/__init__.py +0 -0
- {atsfill-0.0.4.dev89 → atsfill-0.0.4.dev90}/src/atsfill/core/accounts.py +0 -0
- {atsfill-0.0.4.dev89 → atsfill-0.0.4.dev90}/src/atsfill/core/adapter.py +0 -0
- {atsfill-0.0.4.dev89 → atsfill-0.0.4.dev90}/src/atsfill/core/attempts.py +0 -0
- {atsfill-0.0.4.dev89 → atsfill-0.0.4.dev90}/src/atsfill/core/commands.py +0 -0
- {atsfill-0.0.4.dev89 → atsfill-0.0.4.dev90}/src/atsfill/core/matching.py +0 -0
- {atsfill-0.0.4.dev89 → atsfill-0.0.4.dev90}/src/atsfill/core/passwords.py +0 -0
- {atsfill-0.0.4.dev89 → atsfill-0.0.4.dev90}/src/atsfill/core/paths.py +0 -0
- {atsfill-0.0.4.dev89 → atsfill-0.0.4.dev90}/src/atsfill/core/registry.py +0 -0
- {atsfill-0.0.4.dev89 → atsfill-0.0.4.dev90}/src/atsfill/py.typed +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: atsfill
|
|
3
|
-
Version: 0.0.4.
|
|
3
|
+
Version: 0.0.4.dev90
|
|
4
4
|
Summary: Read, dry-run and submit job application forms on hiring systems: Ashby, Greenhouse, Teamtailor, Welcome to the Jungle.
|
|
5
5
|
Keywords: job-applications,ats,ashby,greenhouse,teamtailor,ai-agents,cli
|
|
6
6
|
Author: shbernal
|
|
@@ -65,7 +65,7 @@ It is built for agents with a shell: every command prints one JSON document and
|
|
|
65
65
|
| Teamtailor | no | no |
|
|
66
66
|
| Welcome to the Jungle | yes, `atsfill login wttj` | no |
|
|
67
67
|
|
|
68
|
-
Any other system returns `unsupported`, so the
|
|
68
|
+
Any other system returns `unsupported`, so the consumer can fall back to driving a browser itself.
|
|
69
69
|
|
|
70
70
|
## Why atsfill?
|
|
71
71
|
|
|
@@ -133,7 +133,7 @@ A dry run is not invisible: some systems save answers or the resume as they are
|
|
|
133
133
|
`atsfill status` lists each system's side effects, whether it is allowed to submit, and how far atsfill has verified it: [the systems](docs/adapters/README.md).
|
|
134
134
|
A dry run or a `send` through an adapter no send was verified on carries a `warning`; the send goes ahead anyway.
|
|
135
135
|
|
|
136
|
-
One home can
|
|
136
|
+
One home can send for more than one user: `--user <name>` gives each their own sessions, double-send guard and `[users.<name>]` section in the config file ([ADR 0015](docs/adr/0015-users-and-accounts.md)).
|
|
137
137
|
Given an account's email (`atsfill login <adapter> --email`) and a password source (a `[password-commands]` entry such as `rbw get example.com {email}`, or `--password -` on stdin), atsfill signs in by itself, and again whenever a form command finds the session signed out.
|
|
138
138
|
Where a system's sign-up allows it, `atsfill login <adapter> --create --email <address>` creates the account with a password atsfill generates and keeps in the system's secret store, never printed; creating an account is a side effect, never part of `fill` or `send`.
|
|
139
139
|
|
|
@@ -39,7 +39,7 @@ It is built for agents with a shell: every command prints one JSON document and
|
|
|
39
39
|
| Teamtailor | no | no |
|
|
40
40
|
| Welcome to the Jungle | yes, `atsfill login wttj` | no |
|
|
41
41
|
|
|
42
|
-
Any other system returns `unsupported`, so the
|
|
42
|
+
Any other system returns `unsupported`, so the consumer can fall back to driving a browser itself.
|
|
43
43
|
|
|
44
44
|
## Why atsfill?
|
|
45
45
|
|
|
@@ -107,7 +107,7 @@ A dry run is not invisible: some systems save answers or the resume as they are
|
|
|
107
107
|
`atsfill status` lists each system's side effects, whether it is allowed to submit, and how far atsfill has verified it: [the systems](docs/adapters/README.md).
|
|
108
108
|
A dry run or a `send` through an adapter no send was verified on carries a `warning`; the send goes ahead anyway.
|
|
109
109
|
|
|
110
|
-
One home can
|
|
110
|
+
One home can send for more than one user: `--user <name>` gives each their own sessions, double-send guard and `[users.<name>]` section in the config file ([ADR 0015](docs/adr/0015-users-and-accounts.md)).
|
|
111
111
|
Given an account's email (`atsfill login <adapter> --email`) and a password source (a `[password-commands]` entry such as `rbw get example.com {email}`, or `--password -` on stdin), atsfill signs in by itself, and again whenever a form command finds the session signed out.
|
|
112
112
|
Where a system's sign-up allows it, `atsfill login <adapter> --create --email <address>` creates the account with a password atsfill generates and keeps in the system's secret store, never printed; creating an account is a side effect, never part of `fill` or `send`.
|
|
113
113
|
|
|
@@ -236,7 +236,7 @@ class Phenom:
|
|
|
236
236
|
SideEffect(
|
|
237
237
|
"fill",
|
|
238
238
|
"hiring_system",
|
|
239
|
-
"each step is submitted to Phenom on Next, which keeps
|
|
239
|
+
"each step is submitted to Phenom on Next, which keeps an account profile from the"
|
|
240
240
|
" first step",
|
|
241
241
|
),
|
|
242
242
|
SideEffect("submit", "employer", "the application reaches the employer"),
|
|
@@ -703,7 +703,7 @@ def _fill(page: Page, field: Field, raw: dict[str, Any], answer: Answer) -> None
|
|
|
703
703
|
case "file", _:
|
|
704
704
|
files = [answer] if isinstance(answer, str) else answer
|
|
705
705
|
delete = box.locator(DELETE_FILE)
|
|
706
|
-
while (count := delete.count()) > 0: # the draft's or the profile's files
|
|
706
|
+
while (count := delete.count()) > 0: # the draft's or the account profile's files
|
|
707
707
|
delete.first.click()
|
|
708
708
|
deadline = time.monotonic() + UPLOAD_TIMEOUT_MS / 1000
|
|
709
709
|
while delete.count() == count and time.monotonic() < deadline:
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""Welcome to the Jungle (`welcometothejungle.com` jobs and their `easyapply.jobs` links), as
|
|
2
2
|
docs/adapters/wttj.md observes.
|
|
3
3
|
|
|
4
|
-
Applying needs an account: the form opens in a modal, prefilled from the profile. Nothing is
|
|
4
|
+
Applying needs an account: the form opens in a modal, prefilled from the account profile. Nothing is
|
|
5
5
|
sent before submit, so the read-back is the form's inputs and the CV's shown name.
|
|
6
6
|
"""
|
|
7
7
|
|
|
@@ -118,7 +118,7 @@ class WelcomeToTheJungle:
|
|
|
118
118
|
while location.count() and not location.input_value() and time.monotonic() < deadline:
|
|
119
119
|
page.wait_for_timeout(
|
|
120
120
|
LOCATION_POLL_MS
|
|
121
|
-
) # the profile's location comes last, a second later
|
|
121
|
+
) # the account profile's location comes last, a second later
|
|
122
122
|
fields: dict[str, Field] = {}
|
|
123
123
|
for raw in form.evaluate(_READ):
|
|
124
124
|
if raw["id"] not in fields and (field := _field(raw)) is not None:
|
|
@@ -135,7 +135,7 @@ class WelcomeToTheJungle:
|
|
|
135
135
|
box = target.locator("xpath=..")
|
|
136
136
|
remove = box.locator("button[title=Remove]")
|
|
137
137
|
if remove.count():
|
|
138
|
-
remove.click() # the profile's CV; never made the default
|
|
138
|
+
remove.click() # the account profile's CV; never made the default
|
|
139
139
|
target.set_input_files(answer)
|
|
140
140
|
box.locator(f'[data-id="{answer.rsplit("/", 1)[-1]}"]').wait_for()
|
|
141
141
|
case FieldType.CHOICE, str():
|
|
@@ -175,7 +175,7 @@ def _suggest(page: Page, field: Field, box: Locator, answer: str) -> None:
|
|
|
175
175
|
options = page.locator("[role=option]:visible").filter(has_not=page.locator("svg"))
|
|
176
176
|
for _ in range(SUGGEST_TRIES):
|
|
177
177
|
box.click()
|
|
178
|
-
box.press("Control+a") # `fill("")
|
|
178
|
+
box.press("Control+a") # `fill("")` (select-all, Delete) leaves the account profile's value
|
|
179
179
|
box.press("Backspace")
|
|
180
180
|
box.press_sequentially(answer, delay=KEY_DELAY_MS)
|
|
181
181
|
deadline = time.monotonic() + SUGGEST_TIMEOUT_S
|
|
@@ -124,7 +124,7 @@ def _build_blocker() -> None:
|
|
|
124
124
|
local = f"assets/{BLOCKER_LISTS}/{key}.txt"
|
|
125
125
|
(build / local).parent.mkdir(exist_ok=True)
|
|
126
126
|
(build / local).write_text(_filter_list([*urls, *entry.get("cdnURLs", [])]))
|
|
127
|
-
entry.pop("off", None) # on from the start, the way a fresh profile reads it
|
|
127
|
+
entry.pop("off", None) # on from the start, the way a fresh browser profile reads it
|
|
128
128
|
entry["contentURL"] = [local, *urls]
|
|
129
129
|
entries["atsfill"] = {"content": "filters", "group": "custom", "title": "atsfill",
|
|
130
130
|
"contentURL": [FILTERS]} # fmt: skip
|
|
@@ -34,7 +34,7 @@ class Field:
|
|
|
34
34
|
options: list[str] | None = None
|
|
35
35
|
max_length: int | None = None
|
|
36
36
|
accept: list[str] | None = None
|
|
37
|
-
prefilled: bool = False # holds a value before any answer, from the account
|
|
37
|
+
prefilled: bool = False # holds a value before any answer, from the account profile
|
|
38
38
|
unfillable: str | None = None # why atsfill cannot fill it, a reason (ADR 0014)
|
|
39
39
|
|
|
40
40
|
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
# send_verified
|
|
14
14
|
# unsupported
|
|
15
15
|
# date, version, commit, by, reason: a round that ended without the adapter passing
|
|
16
|
-
# the gate (ADR 0013), the known gap that stopped it
|
|
16
|
+
# the adapter gate (ADR 0013), the known gap that stopped it
|
|
17
17
|
# out_of_scope
|
|
18
18
|
# reason, date: a system atsfill will never fill, it needs the user live (ADR 0014)
|
|
19
19
|
# send_broken
|
|
@@ -315,7 +315,7 @@ dry_run_verified = { date = 2026-10-22, version = "0.0.3", by = "adapter_gate",
|
|
|
315
315
|
[isolvedhire]
|
|
316
316
|
kind = "ats"
|
|
317
317
|
share = 0.0047
|
|
318
|
-
account = "to_read" # Apply now first verifies the email or phone with
|
|
318
|
+
account = "to_read" # Apply now first verifies the email or phone with an OTP, then creates an account on the employer's careers site; the application wizard (/apply/<job>-0.php) redirects to that site's login until then
|
|
319
319
|
hosting = ["hosted"]
|
|
320
320
|
mapped = { date = 2026-10-20, version = "0.0.2", by = "agent", commit = "7934b25" }
|
|
321
321
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|