odda 0.44.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.
Files changed (40) hide show
  1. odda-0.44.0/LICENSE +21 -0
  2. odda-0.44.0/PKG-INFO +164 -0
  3. odda-0.44.0/README.md +134 -0
  4. odda-0.44.0/pyproject.toml +147 -0
  5. odda-0.44.0/setup.cfg +4 -0
  6. odda-0.44.0/src/odda/__init__.py +15 -0
  7. odda-0.44.0/src/odda/__main__.py +6 -0
  8. odda-0.44.0/src/odda/browser.py +1989 -0
  9. odda-0.44.0/src/odda/chrome_args.py +141 -0
  10. odda-0.44.0/src/odda/cli.py +98 -0
  11. odda-0.44.0/src/odda/coverage.py +213 -0
  12. odda-0.44.0/src/odda/docs/__init__.py +1 -0
  13. odda-0.44.0/src/odda/docs/dynamic-analysis.md +151 -0
  14. odda-0.44.0/src/odda/docs/flows.md +51 -0
  15. odda-0.44.0/src/odda/docs/proxy-scripts.md +46 -0
  16. odda-0.44.0/src/odda/docs/recipes.md +306 -0
  17. odda-0.44.0/src/odda/docs/request-crafting.md +97 -0
  18. odda-0.44.0/src/odda/docs/userscripts.md +26 -0
  19. odda-0.44.0/src/odda/flowstore.py +555 -0
  20. odda-0.44.0/src/odda/logpoint.py +191 -0
  21. odda-0.44.0/src/odda/mcp.py +1086 -0
  22. odda-0.44.0/src/odda/proxy.py +143 -0
  23. odda-0.44.0/src/odda/proxyscript.py +234 -0
  24. odda-0.44.0/src/odda/request/__init__.py +16 -0
  25. odda-0.44.0/src/odda/request/api.py +665 -0
  26. odda-0.44.0/src/odda/request/h1.py +459 -0
  27. odda-0.44.0/src/odda/request/h2.py +431 -0
  28. odda-0.44.0/src/odda/request/parsing.py +135 -0
  29. odda-0.44.0/src/odda/request/response.py +95 -0
  30. odda-0.44.0/src/odda/request/storage.py +62 -0
  31. odda-0.44.0/src/odda/request/types.py +73 -0
  32. odda-0.44.0/src/odda/userscript.py +171 -0
  33. odda-0.44.0/src/odda/userscripts/default/logpoint-helpers.js +79 -0
  34. odda-0.44.0/src/odda/wrap.py +467 -0
  35. odda-0.44.0/src/odda.egg-info/PKG-INFO +164 -0
  36. odda-0.44.0/src/odda.egg-info/SOURCES.txt +38 -0
  37. odda-0.44.0/src/odda.egg-info/dependency_links.txt +1 -0
  38. odda-0.44.0/src/odda.egg-info/entry_points.txt +2 -0
  39. odda-0.44.0/src/odda.egg-info/requires.txt +11 -0
  40. odda-0.44.0/src/odda.egg-info/top_level.txt +1 -0
odda-0.44.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Zakaria Ounissi
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
odda-0.44.0/PKG-INFO ADDED
@@ -0,0 +1,164 @@
1
+ Metadata-Version: 2.4
2
+ Name: odda
3
+ Version: 0.44.0
4
+ Summary: Browser automation and HTTP traffic capture for AI agents (MCP server)
5
+ Author-email: Zakaria Ounissi <ounissizakaria@gmail.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/ounissi-zakaria/odda
8
+ Project-URL: Repository, https://github.com/ounissi-zakaria/odda
9
+ Project-URL: Issues, https://github.com/ounissi-zakaria/odda/issues
10
+ Keywords: browser-automation,http-proxy,mcp,ai-agents,traffic-capture,bug-bounty,pentest,penetration-testing,security-research
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Programming Language :: Python :: 3 :: Only
13
+ Classifier: Programming Language :: Python :: 3.14
14
+ Classifier: Topic :: Internet :: WWW/HTTP :: Browsers
15
+ Classifier: Topic :: Security
16
+ Requires-Python: >=3.14
17
+ Description-Content-Type: text/markdown
18
+ License-File: LICENSE
19
+ Requires-Dist: mitmproxy>=11.0.2
20
+ Requires-Dist: patchright==1.62.1
21
+ Requires-Dist: h2>=4.0.0
22
+ Requires-Dist: mcp==2.1.1
23
+ Provides-Extra: dev
24
+ Requires-Dist: ruff>=0.15.11; extra == "dev"
25
+ Requires-Dist: hypercorn>=0.18.0; extra == "dev"
26
+ Requires-Dist: pytest>=9.0; extra == "dev"
27
+ Requires-Dist: pytest-asyncio>=1.0; extra == "dev"
28
+ Requires-Dist: pytest-xdist>=3.6; extra == "dev"
29
+ Dynamic: license-file
30
+
31
+ # odda
32
+
33
+ `odda` is a toolkit for agent-driven web security research, composable from a single MCP server. It gives an AI agent a real Chrome it can drive and observe, a transparent mitmproxy that writes every flow to disk, and a raw-HTTP send path for smuggling and race conditions.
34
+
35
+ - **Drive Chrome** — open, navigate, run JS, screenshot, and interact with the page by accessibility-tree refs.
36
+ - **Capture every flow** — a transparent proxy writes every request/response to disk, including media bodies browser capture drops.
37
+ - **Observe JS in progress** — wrap functions/properties, plant source logpoints, record block coverage.
38
+ - **Craft raw HTTP** — byte-faithful sends for smuggling, parser-differentials, and races.
39
+
40
+ ## Requirements
41
+
42
+ - Python 3.14+
43
+ - Chrome or Chromium installed and on `PATH`
44
+
45
+ ## Installation
46
+
47
+ ```bash
48
+ uv tool install git+https://github.com/ounissi-zakaria/odda.git
49
+ ```
50
+
51
+ This installs the `odda` command (an MCP server plus one helper subcommand, with `--version` for the version probe). Then register the server with your harness — one MCP config entry, and every odda capability becomes typed tools your agent can call.
52
+
53
+ ### Harness configuration
54
+
55
+ All configs spawn the same stdio server: `odda mcp`. One MCP process = one odda session with its own Chrome, proxy, and `.odda/` data dir (created lazily under the process's working directory on first use).
56
+
57
+ **Claude Code** — project `.mcp.json` (or `~/.claude.json` under `mcpServers`):
58
+
59
+ ```json
60
+ {
61
+ "mcpServers": {
62
+ "odda": {
63
+ "command": "odda",
64
+ "args": ["mcp"]
65
+ }
66
+ }
67
+ }
68
+ ```
69
+
70
+ **OpenCode** — `~/.config/opencode/opencode.json`:
71
+
72
+ ```json
73
+ {
74
+ "mcp": {
75
+ "odda": {
76
+ "type": "local",
77
+ "command": ["odda", "mcp"]
78
+ }
79
+ }
80
+ }
81
+ ```
82
+
83
+ **pi** — install the MCP adapter once (`pi install npm:pi-mcp-adapter`), then add to project `.mcp.json` (same `mcpServers` shape as Claude Code):
84
+
85
+ ```json
86
+ {
87
+ "mcpServers": {
88
+ "odda": {
89
+ "command": "odda",
90
+ "args": ["mcp"]
91
+ }
92
+ }
93
+ }
94
+ ```
95
+
96
+ ### Chrome profile (one-time)
97
+
98
+ To give odda's isolated browser sessions a base profile (cookies, extensions, preferences):
99
+
100
+ ```bash
101
+ odda init-chrome-profile
102
+ ```
103
+
104
+ This opens a visible Chrome window pointed at odda's base profile directory. Log in, install extensions, and set preferences as you want them; close the window when done. odda copies this profile into each isolated browser session opened by `browser_open`. Skip this step to start from a clean profile each time.
105
+
106
+ ## Usage
107
+
108
+ Agents drive odda through the MCP tools (`browser_open`, `navigate`, `page_snapshot`, `request_send`, ...). IDs are integers, monotonic within a session, and never reused; every browser/tab tool takes explicit `browser_id` / `tab_id` parameters.
109
+
110
+ A typical session:
111
+
112
+ ```
113
+ browser_open → {browser_id, tab_id}
114
+ navigate → tab loads https://target.example/
115
+ read .odda/flows/flows.jsonl → every request/response captured on disk
116
+ request_clone + request_send → the Burp Repeater loop, as tools
117
+ ```
118
+
119
+ All captured traffic is stored under `.odda/flows/<id>/` — request bytes, response body, headers, timing — plus the append-only `flows.jsonl` index. Grep the index to find flows; read `.odda/flows/<id>/response_body.*` for the body (including image/video/audio/font Content-Types that browser capture drops).
120
+
121
+ ## Features
122
+
123
+ ### Drive Chrome
124
+
125
+ A real Chrome (via patchright/Playwright), headless by default:
126
+
127
+ - **Open / navigate / eval / wait-for / screenshot** — drive the browser and run JS in the page.
128
+ - **Ref-driven page interaction** — `page_snapshot` returns the accessibility tree with `[ref=eN]` tags; pass the ref to `page_click` / `page_fill` / `page_hover` / `page_upload`. `page_click` and `page_hover` also accept viewport coordinates as a raw trusted event for targets the a11y tree can't name. Cross-iframe is transparent.
129
+ - **Per-browser userscripts** — JS that auto-runs at `document_start` on every navigation, before the page's own scripts.
130
+ - **Dialog blocking** — `alert` / `confirm` / `prompt` / `beforeunload` stay open until handled: the action that opens one returns its details, other tools on that tab wait, and every same-browser result lists open dialogs. Resolve with `dialog_handle` (accept/dismiss, prompt text); a human closing it in a headed window works too.
131
+
132
+ ### Capture every flow
133
+
134
+ A transparent mitmproxy sits between Chrome and the network; driving the browser *is* traffic capture. Every request/response is saved under `.odda/flows/<id>/`; the index is `.odda/flows/flows.jsonl`. See the `odda://docs/flows` resource for the file layout and schema.
135
+
136
+ ### Observe JS in progress
137
+
138
+ Three lenses, chosen by what you know:
139
+
140
+ - **Wrap** — wrap a named function or property; record each call/access with `this`, `args`, `ret`, and `stack`. Takes effect on the next navigation.
141
+ - **Logpoint** — plant a non-pausing observation at a source `url` + `line` + `col`; the expression is evaluated in the paused frame's scope, so it reads locals by name.
142
+ - **Coverage** — record which code blocks execute across one or more navigations; start, trigger behavior, snapshot or stop.
143
+
144
+ Wrap and Logpoint records wipe on navigation — dump before navigating again. The `odda://docs/dynamic-analysis` resource documents the full surface; `odda://docs/recipes` has a worked example tracing untrusted DOM data to a sink.
145
+
146
+ ### Craft raw HTTP
147
+
148
+ Byte-faithful raw HTTP sends, bypassing the browser — the Burp Repeater model, as tools:
149
+
150
+ - **Wire-verbatim HTTP/1.1** — the request file *is* the wire; nothing is re-framed.
151
+ - **HTTP/2 frame-source** — the request file is parsed into H2 frames; a custom line terminator lets a literal CRLF live inside an H2 pseudo-header for downgrade smuggling.
152
+ - **Clone or craft** — `request_clone` copies a captured flow's exact bytes; `request_new` starts an empty file.
153
+ - **Multi-name pipeline** — pass several names to send several requests on one connection (H1 keep-alive, or H2 concurrent stream-multiplex) for same-connection attacks.
154
+ - **Concurrent send** — `repeat` fires N copies of one request concurrently for race conditions and limit-overrun attacks.
155
+ - **`fix_content_length`** — recompute Content-Length after body edits.
156
+
157
+ See the `odda://docs/request-crafting` resource for framing details.
158
+
159
+ ## How it works
160
+
161
+ - `odda mcp` is the **only** odda process: a stdio MCP server whose lifespan owns the proxy, the browser manager, and the flow storage. Your harness spawns it per agent session; closing the session tears everything down.
162
+ - The remaining CLI surface is helpers, not the automation surface: `odda init-chrome-profile` (interactive, human-run) and `odda --version`/`-V`.
163
+ - Project state lives in `.odda/` under the MCP process's working directory: `flows/`, `requests/`, `browsers/`. It's created lazily on the first state-producing call, not when the server boots.
164
+ - Errors from tools are odda's messages verbatim (as tool errors); unanticipated crashes log their traceback to the server's stderr, which the harness captures.
odda-0.44.0/README.md ADDED
@@ -0,0 +1,134 @@
1
+ # odda
2
+
3
+ `odda` is a toolkit for agent-driven web security research, composable from a single MCP server. It gives an AI agent a real Chrome it can drive and observe, a transparent mitmproxy that writes every flow to disk, and a raw-HTTP send path for smuggling and race conditions.
4
+
5
+ - **Drive Chrome** — open, navigate, run JS, screenshot, and interact with the page by accessibility-tree refs.
6
+ - **Capture every flow** — a transparent proxy writes every request/response to disk, including media bodies browser capture drops.
7
+ - **Observe JS in progress** — wrap functions/properties, plant source logpoints, record block coverage.
8
+ - **Craft raw HTTP** — byte-faithful sends for smuggling, parser-differentials, and races.
9
+
10
+ ## Requirements
11
+
12
+ - Python 3.14+
13
+ - Chrome or Chromium installed and on `PATH`
14
+
15
+ ## Installation
16
+
17
+ ```bash
18
+ uv tool install git+https://github.com/ounissi-zakaria/odda.git
19
+ ```
20
+
21
+ This installs the `odda` command (an MCP server plus one helper subcommand, with `--version` for the version probe). Then register the server with your harness — one MCP config entry, and every odda capability becomes typed tools your agent can call.
22
+
23
+ ### Harness configuration
24
+
25
+ All configs spawn the same stdio server: `odda mcp`. One MCP process = one odda session with its own Chrome, proxy, and `.odda/` data dir (created lazily under the process's working directory on first use).
26
+
27
+ **Claude Code** — project `.mcp.json` (or `~/.claude.json` under `mcpServers`):
28
+
29
+ ```json
30
+ {
31
+ "mcpServers": {
32
+ "odda": {
33
+ "command": "odda",
34
+ "args": ["mcp"]
35
+ }
36
+ }
37
+ }
38
+ ```
39
+
40
+ **OpenCode** — `~/.config/opencode/opencode.json`:
41
+
42
+ ```json
43
+ {
44
+ "mcp": {
45
+ "odda": {
46
+ "type": "local",
47
+ "command": ["odda", "mcp"]
48
+ }
49
+ }
50
+ }
51
+ ```
52
+
53
+ **pi** — install the MCP adapter once (`pi install npm:pi-mcp-adapter`), then add to project `.mcp.json` (same `mcpServers` shape as Claude Code):
54
+
55
+ ```json
56
+ {
57
+ "mcpServers": {
58
+ "odda": {
59
+ "command": "odda",
60
+ "args": ["mcp"]
61
+ }
62
+ }
63
+ }
64
+ ```
65
+
66
+ ### Chrome profile (one-time)
67
+
68
+ To give odda's isolated browser sessions a base profile (cookies, extensions, preferences):
69
+
70
+ ```bash
71
+ odda init-chrome-profile
72
+ ```
73
+
74
+ This opens a visible Chrome window pointed at odda's base profile directory. Log in, install extensions, and set preferences as you want them; close the window when done. odda copies this profile into each isolated browser session opened by `browser_open`. Skip this step to start from a clean profile each time.
75
+
76
+ ## Usage
77
+
78
+ Agents drive odda through the MCP tools (`browser_open`, `navigate`, `page_snapshot`, `request_send`, ...). IDs are integers, monotonic within a session, and never reused; every browser/tab tool takes explicit `browser_id` / `tab_id` parameters.
79
+
80
+ A typical session:
81
+
82
+ ```
83
+ browser_open → {browser_id, tab_id}
84
+ navigate → tab loads https://target.example/
85
+ read .odda/flows/flows.jsonl → every request/response captured on disk
86
+ request_clone + request_send → the Burp Repeater loop, as tools
87
+ ```
88
+
89
+ All captured traffic is stored under `.odda/flows/<id>/` — request bytes, response body, headers, timing — plus the append-only `flows.jsonl` index. Grep the index to find flows; read `.odda/flows/<id>/response_body.*` for the body (including image/video/audio/font Content-Types that browser capture drops).
90
+
91
+ ## Features
92
+
93
+ ### Drive Chrome
94
+
95
+ A real Chrome (via patchright/Playwright), headless by default:
96
+
97
+ - **Open / navigate / eval / wait-for / screenshot** — drive the browser and run JS in the page.
98
+ - **Ref-driven page interaction** — `page_snapshot` returns the accessibility tree with `[ref=eN]` tags; pass the ref to `page_click` / `page_fill` / `page_hover` / `page_upload`. `page_click` and `page_hover` also accept viewport coordinates as a raw trusted event for targets the a11y tree can't name. Cross-iframe is transparent.
99
+ - **Per-browser userscripts** — JS that auto-runs at `document_start` on every navigation, before the page's own scripts.
100
+ - **Dialog blocking** — `alert` / `confirm` / `prompt` / `beforeunload` stay open until handled: the action that opens one returns its details, other tools on that tab wait, and every same-browser result lists open dialogs. Resolve with `dialog_handle` (accept/dismiss, prompt text); a human closing it in a headed window works too.
101
+
102
+ ### Capture every flow
103
+
104
+ A transparent mitmproxy sits between Chrome and the network; driving the browser *is* traffic capture. Every request/response is saved under `.odda/flows/<id>/`; the index is `.odda/flows/flows.jsonl`. See the `odda://docs/flows` resource for the file layout and schema.
105
+
106
+ ### Observe JS in progress
107
+
108
+ Three lenses, chosen by what you know:
109
+
110
+ - **Wrap** — wrap a named function or property; record each call/access with `this`, `args`, `ret`, and `stack`. Takes effect on the next navigation.
111
+ - **Logpoint** — plant a non-pausing observation at a source `url` + `line` + `col`; the expression is evaluated in the paused frame's scope, so it reads locals by name.
112
+ - **Coverage** — record which code blocks execute across one or more navigations; start, trigger behavior, snapshot or stop.
113
+
114
+ Wrap and Logpoint records wipe on navigation — dump before navigating again. The `odda://docs/dynamic-analysis` resource documents the full surface; `odda://docs/recipes` has a worked example tracing untrusted DOM data to a sink.
115
+
116
+ ### Craft raw HTTP
117
+
118
+ Byte-faithful raw HTTP sends, bypassing the browser — the Burp Repeater model, as tools:
119
+
120
+ - **Wire-verbatim HTTP/1.1** — the request file *is* the wire; nothing is re-framed.
121
+ - **HTTP/2 frame-source** — the request file is parsed into H2 frames; a custom line terminator lets a literal CRLF live inside an H2 pseudo-header for downgrade smuggling.
122
+ - **Clone or craft** — `request_clone` copies a captured flow's exact bytes; `request_new` starts an empty file.
123
+ - **Multi-name pipeline** — pass several names to send several requests on one connection (H1 keep-alive, or H2 concurrent stream-multiplex) for same-connection attacks.
124
+ - **Concurrent send** — `repeat` fires N copies of one request concurrently for race conditions and limit-overrun attacks.
125
+ - **`fix_content_length`** — recompute Content-Length after body edits.
126
+
127
+ See the `odda://docs/request-crafting` resource for framing details.
128
+
129
+ ## How it works
130
+
131
+ - `odda mcp` is the **only** odda process: a stdio MCP server whose lifespan owns the proxy, the browser manager, and the flow storage. Your harness spawns it per agent session; closing the session tears everything down.
132
+ - The remaining CLI surface is helpers, not the automation surface: `odda init-chrome-profile` (interactive, human-run) and `odda --version`/`-V`.
133
+ - Project state lives in `.odda/` under the MCP process's working directory: `flows/`, `requests/`, `browsers/`. It's created lazily on the first state-producing call, not when the server boots.
134
+ - Errors from tools are odda's messages verbatim (as tool errors); unanticipated crashes log their traceback to the server's stderr, which the harness captures.
@@ -0,0 +1,147 @@
1
+ [project]
2
+ name = "odda"
3
+ version = "0.44.0"
4
+ description = "Browser automation and HTTP traffic capture for AI agents (MCP server)"
5
+ readme = "README.md"
6
+ requires-python = ">=3.14"
7
+ license = "MIT"
8
+ license-files = ["LICENSE"]
9
+ authors = [{ name = "Zakaria Ounissi", email = "ounissizakaria@gmail.com" }]
10
+ keywords = ["browser-automation", "http-proxy", "mcp", "ai-agents", "traffic-capture", "bug-bounty", "pentest", "penetration-testing", "security-research"]
11
+ classifiers = [
12
+ "Development Status :: 4 - Beta",
13
+ "Programming Language :: Python :: 3 :: Only",
14
+ "Programming Language :: Python :: 3.14",
15
+ "Topic :: Internet :: WWW/HTTP :: Browsers",
16
+ "Topic :: Security",
17
+ ]
18
+ dependencies = [
19
+ "mitmproxy>=11.0.2",
20
+ "patchright==1.62.1",
21
+ "h2>=4.0.0",
22
+ "mcp==2.1.1",
23
+ ]
24
+
25
+ [project.scripts]
26
+ odda = "odda.cli:main"
27
+
28
+ [project.urls]
29
+ Homepage = "https://github.com/ounissi-zakaria/odda"
30
+ Repository = "https://github.com/ounissi-zakaria/odda"
31
+ Issues = "https://github.com/ounissi-zakaria/odda/issues"
32
+
33
+ [project.optional-dependencies]
34
+ dev = [
35
+ "ruff>=0.15.11",
36
+ "hypercorn>=0.18.0",
37
+ "pytest>=9.0",
38
+ "pytest-asyncio>=1.0",
39
+ "pytest-xdist>=3.6",
40
+ ]
41
+ [tool.setuptools.packages.find]
42
+ where = ["src"]
43
+
44
+ [tool.setuptools.package-dir]
45
+ "" = "src"
46
+
47
+ [tool.setuptools.package-data]
48
+ odda = ["docs/*.md", "userscripts/default/*.js"]
49
+
50
+ [tool.ruff]
51
+ target-version = "py311"
52
+ line-length = 88
53
+ indent-width = 4
54
+ exclude = ["tests/e2e/*.sh"]
55
+
56
+ [tool.ruff.lint]
57
+ select = [
58
+ "E",
59
+ "W",
60
+ "F",
61
+ "I",
62
+ "N",
63
+ "D",
64
+ "S",
65
+ "B",
66
+ "C4",
67
+ "UP",
68
+ "SIM",
69
+ "TCH",
70
+ "PTH",
71
+ "ERA",
72
+ "RUF",
73
+ "ARG",
74
+ "PL",
75
+ "PERF",
76
+ "FURB",
77
+ "TRY",
78
+ "EM",
79
+ "RET",
80
+ "SLF",
81
+ "TID",
82
+ "RSE",
83
+ "FBT",
84
+ "COM",
85
+ "C90",
86
+ "INP",
87
+ "PIE",
88
+ "T20",
89
+ "PGH",
90
+ "FAST",
91
+ ]
92
+ ignore = [
93
+ "D100",
94
+ "D104",
95
+ "COM812",
96
+ "ISC001",
97
+ "T201",
98
+ "EM101",
99
+ "EM102",
100
+ "TRY003",
101
+ ]
102
+
103
+ [tool.ruff.lint.pydocstyle]
104
+ convention = "google"
105
+
106
+ [tool.ruff.lint.mccabe]
107
+ max-complexity = 10
108
+
109
+ [tool.ruff.lint.pylint]
110
+ max-args = 6
111
+ max-branches = 15
112
+ max-returns = 8
113
+ max-statements = 60
114
+ max-public-methods = 25
115
+
116
+ [tool.ruff.format]
117
+ quote-style = "double"
118
+ indent-style = "space"
119
+ skip-magic-trailing-comma = false
120
+ line-ending = "auto"
121
+
122
+ [tool.ruff.lint.isort]
123
+ known-first-party = ["odda"]
124
+ combine-as-imports = true
125
+ split-on-trailing-comma = false
126
+
127
+ [tool.ruff.lint.per-file-ignores]
128
+ "src/odda/cli.py" = ["PLC0415"]
129
+ "src/odda/flowstore.py" = ["PLW0603", "PLR0913"]
130
+ "src/odda/request/*.py" = ["PLR0913", "PLR2004", "C901", "PLR0912", "PLR0915", "TC003"]
131
+ "src/odda/wrap.py" = ["E501"]
132
+ "src/odda/logpoint.py" = ["E501", "PLR0913"]
133
+ # F401's odda_session flagging is the import-shadow pattern (each test's
134
+ # fixture parameter redefines the imported factory; ruff can't model
135
+ # pytest fixtures) — covered by the F811 ignore; modules use it in-body.
136
+ "tests/e2e/conftest.py" = ["D107", "D403", "D205", "D209", "D202", "D402", "PLC0415", "S101", "S603", "S607", "SLF001", "SIM300", "E501", "E741", "TC003", "RUF100", "W292"]
137
+ "tests/e2e/test_*.py" = ["S101", "PLR2004", "E501", "E741", "D415", "D403", "D205", "D209", "PLC0415", "RUF100", "RUF003", "RUF059", "SIM300", "W292", "INP001", "S105", "S311", "F811", "F401", "ARG001", "D301", "W605", "TC003"]
138
+ "tests/e2e/fixtures/dyn_asgi.py" = ["INP001", "ARG001", "S108"]
139
+ "tests/e2e/fixtures/echo_path_h2server.py" = ["INP001", "S110"]
140
+ "tests/e2e/fixtures/ws_header_h2server.py" = ["INP001", "S110"]
141
+ "tests/e2e/fixtures/h2_cookie_client.py" = ["INP001"]
142
+ "tests/e2e/fixtures/slow_load_server.py" = ["INP001"]
143
+ "tests/e2e/fixtures/close_after_first_server.py" = ["INP001", "S110"]
144
+ "tests/e2e/fixtures/close_without_response_server.py" = ["INP001", "S110"]
145
+ [build-system]
146
+ requires = ["setuptools>=77"]
147
+ build-backend = "setuptools.build_meta"
odda-0.44.0/setup.cfg ADDED
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,15 @@
1
+ """odda - Browser automation and HTTP traffic capture CLI for AI agents."""
2
+
3
+ __version__ = "0.44.0"
4
+
5
+ #: Playwright ``page.goto`` lifecycle events accepted by navigate, in firing
6
+ #: order. The single source of truth for the MCP server's ``navigate``
7
+ #: param validation and error message; the browser layer passes the value
8
+ #: through to Playwright. Lives here so importing it stays lightweight —
9
+ #: no pull-in of the patchright-backed ``odda.browser`` module.
10
+ NAVIGATE_WAIT_UNTIL_EVENTS: tuple[str, ...] = (
11
+ "commit",
12
+ "domcontentloaded",
13
+ "load",
14
+ "networkidle",
15
+ )
@@ -0,0 +1,6 @@
1
+ """Allow running odda as a module: python -m odda."""
2
+
3
+ from odda.cli import main
4
+
5
+ if __name__ == "__main__":
6
+ main()