openbrowse 1.8.1__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 (119) hide show
  1. openbrowse-1.8.1/.gitignore +37 -0
  2. openbrowse-1.8.1/CITATION.cff +23 -0
  3. openbrowse-1.8.1/LICENSE +21 -0
  4. openbrowse-1.8.1/PKG-INFO +254 -0
  5. openbrowse-1.8.1/README.md +196 -0
  6. openbrowse-1.8.1/openbrowse/__init__.py +26 -0
  7. openbrowse-1.8.1/openbrowse/agent/__init__.py +0 -0
  8. openbrowse-1.8.1/openbrowse/agent/activity.py +96 -0
  9. openbrowse-1.8.1/openbrowse/agent/browser_cdp.py +64 -0
  10. openbrowse-1.8.1/openbrowse/agent/captcha/__init__.py +18 -0
  11. openbrowse-1.8.1/openbrowse/agent/captcha/base.py +232 -0
  12. openbrowse-1.8.1/openbrowse/agent/captcha/bridge.py +211 -0
  13. openbrowse-1.8.1/openbrowse/agent/captcha/cdp.py +266 -0
  14. openbrowse-1.8.1/openbrowse/agent/captcha/client.py +41 -0
  15. openbrowse-1.8.1/openbrowse/agent/captcha/pipeline.py +252 -0
  16. openbrowse-1.8.1/openbrowse/agent/captcha/probe.py +213 -0
  17. openbrowse-1.8.1/openbrowse/agent/captcha/registry.py +82 -0
  18. openbrowse-1.8.1/openbrowse/agent/captcha/strategies/__init__.py +14 -0
  19. openbrowse-1.8.1/openbrowse/agent/captcha/strategies/awswaf.py +102 -0
  20. openbrowse-1.8.1/openbrowse/agent/captcha/strategies/datadome.py +30 -0
  21. openbrowse-1.8.1/openbrowse/agent/captcha/strategies/geetest.py +165 -0
  22. openbrowse-1.8.1/openbrowse/agent/captcha/strategies/hcaptcha.py +31 -0
  23. openbrowse-1.8.1/openbrowse/agent/captcha/strategies/imagetotext.py +55 -0
  24. openbrowse-1.8.1/openbrowse/agent/captcha/strategies/mtcaptcha.py +55 -0
  25. openbrowse-1.8.1/openbrowse/agent/captcha/strategies/recaptcha.py +276 -0
  26. openbrowse-1.8.1/openbrowse/agent/captcha/strategies/turnstile.py +47 -0
  27. openbrowse-1.8.1/openbrowse/agent/captcha/tools.py +153 -0
  28. openbrowse-1.8.1/openbrowse/agent/code_stream.py +212 -0
  29. openbrowse-1.8.1/openbrowse/agent/cost.py +142 -0
  30. openbrowse-1.8.1/openbrowse/agent/leak_repair.py +419 -0
  31. openbrowse-1.8.1/openbrowse/agent/live.py +237 -0
  32. openbrowse-1.8.1/openbrowse/agent/output_store.py +599 -0
  33. openbrowse-1.8.1/openbrowse/agent/pool.py +89 -0
  34. openbrowse-1.8.1/openbrowse/agent/runner.py +2653 -0
  35. openbrowse-1.8.1/openbrowse/agent/schema.py +276 -0
  36. openbrowse-1.8.1/openbrowse/agent/textguard.py +13 -0
  37. openbrowse-1.8.1/openbrowse/agent/tools.py +4025 -0
  38. openbrowse-1.8.1/openbrowse/api/__init__.py +0 -0
  39. openbrowse-1.8.1/openbrowse/api/profiles.py +147 -0
  40. openbrowse-1.8.1/openbrowse/api/sessions.py +444 -0
  41. openbrowse-1.8.1/openbrowse/auth.py +120 -0
  42. openbrowse-1.8.1/openbrowse/auth_throttle.py +101 -0
  43. openbrowse-1.8.1/openbrowse/browser/__init__.py +12 -0
  44. openbrowse-1.8.1/openbrowse/browser/factory.py +256 -0
  45. openbrowse-1.8.1/openbrowse/browser/vnc.py +27 -0
  46. openbrowse-1.8.1/openbrowse/cli.py +184 -0
  47. openbrowse-1.8.1/openbrowse/config.py +150 -0
  48. openbrowse-1.8.1/openbrowse/dashboard/__init__.py +0 -0
  49. openbrowse-1.8.1/openbrowse/dashboard/import_routes.py +182 -0
  50. openbrowse-1.8.1/openbrowse/dashboard/lifecycle.py +43 -0
  51. openbrowse-1.8.1/openbrowse/dashboard/routes.py +1253 -0
  52. openbrowse-1.8.1/openbrowse/dashboard/setup_routes.py +125 -0
  53. openbrowse-1.8.1/openbrowse/dashboard/static/agents.js +409 -0
  54. openbrowse-1.8.1/openbrowse/dashboard/static/openbrowse.css +308 -0
  55. openbrowse-1.8.1/openbrowse/dashboard/static/openbrowse.ico +0 -0
  56. openbrowse-1.8.1/openbrowse/dashboard/static/openbrowse.svg +8 -0
  57. openbrowse-1.8.1/openbrowse/dashboard/templates/_message_rows.html +31 -0
  58. openbrowse-1.8.1/openbrowse/dashboard/templates/_session_rows.html +45 -0
  59. openbrowse-1.8.1/openbrowse/dashboard/templates/base.html +257 -0
  60. openbrowse-1.8.1/openbrowse/dashboard/templates/import.html +303 -0
  61. openbrowse-1.8.1/openbrowse/dashboard/templates/profiles.html +313 -0
  62. openbrowse-1.8.1/openbrowse/dashboard/templates/restarting.html +77 -0
  63. openbrowse-1.8.1/openbrowse/dashboard/templates/run.html +352 -0
  64. openbrowse-1.8.1/openbrowse/dashboard/templates/session_detail.html +1157 -0
  65. openbrowse-1.8.1/openbrowse/dashboard/templates/sessions.html +369 -0
  66. openbrowse-1.8.1/openbrowse/dashboard/templates/settings.html +360 -0
  67. openbrowse-1.8.1/openbrowse/dashboard/templates/setup.html +108 -0
  68. openbrowse-1.8.1/openbrowse/db/__init__.py +0 -0
  69. openbrowse-1.8.1/openbrowse/db/crud.py +467 -0
  70. openbrowse-1.8.1/openbrowse/db/models.py +120 -0
  71. openbrowse-1.8.1/openbrowse/hostinfo.py +256 -0
  72. openbrowse-1.8.1/openbrowse/main.py +151 -0
  73. openbrowse-1.8.1/openbrowse/profiles/__init__.py +0 -0
  74. openbrowse-1.8.1/openbrowse/profiles/cloud_export.py +216 -0
  75. openbrowse-1.8.1/openbrowse/profiles/import_jobs.py +79 -0
  76. openbrowse-1.8.1/openbrowse/profiles/importer.py +78 -0
  77. openbrowse-1.8.1/openbrowse/profiles/storage.py +104 -0
  78. openbrowse-1.8.1/openbrowse/scripts/host_tune.sh +116 -0
  79. openbrowse-1.8.1/openbrowse/service.py +121 -0
  80. openbrowse-1.8.1/openbrowse/system_metrics.py +179 -0
  81. openbrowse-1.8.1/openbrowse/updates.py +199 -0
  82. openbrowse-1.8.1/pyproject.toml +58 -0
  83. openbrowse-1.8.1/scripts/__init__.py +0 -0
  84. openbrowse-1.8.1/scripts/import_profiles.py +90 -0
  85. openbrowse-1.8.1/scripts/set_version.py +161 -0
  86. openbrowse-1.8.1/tests/conftest.py +12 -0
  87. openbrowse-1.8.1/tests/fixtures/agent_activity_harness.mjs +180 -0
  88. openbrowse-1.8.1/tests/fixtures/cloud_output_schema_1.json +100 -0
  89. openbrowse-1.8.1/tests/fixtures/cloud_output_schema_2.json +291 -0
  90. openbrowse-1.8.1/tests/fixtures/leak_bleed_run3.json +10 -0
  91. openbrowse-1.8.1/tests/test_activity.py +38 -0
  92. openbrowse-1.8.1/tests/test_auth_throttle.py +205 -0
  93. openbrowse-1.8.1/tests/test_browser.py +213 -0
  94. openbrowse-1.8.1/tests/test_captcha.py +764 -0
  95. openbrowse-1.8.1/tests/test_cli.py +95 -0
  96. openbrowse-1.8.1/tests/test_config.py +102 -0
  97. openbrowse-1.8.1/tests/test_cost.py +108 -0
  98. openbrowse-1.8.1/tests/test_dashboard.py +1184 -0
  99. openbrowse-1.8.1/tests/test_dashboard_run.py +220 -0
  100. openbrowse-1.8.1/tests/test_db.py +100 -0
  101. openbrowse-1.8.1/tests/test_host_tune.py +110 -0
  102. openbrowse-1.8.1/tests/test_hostinfo.py +179 -0
  103. openbrowse-1.8.1/tests/test_import_flow.py +189 -0
  104. openbrowse-1.8.1/tests/test_keep_alive_worker.py +557 -0
  105. openbrowse-1.8.1/tests/test_leak_repair.py +410 -0
  106. openbrowse-1.8.1/tests/test_live_sessions.py +235 -0
  107. openbrowse-1.8.1/tests/test_output_store.py +448 -0
  108. openbrowse-1.8.1/tests/test_pool.py +118 -0
  109. openbrowse-1.8.1/tests/test_profiles_api.py +77 -0
  110. openbrowse-1.8.1/tests/test_profiles_import.py +176 -0
  111. openbrowse-1.8.1/tests/test_runner.py +1868 -0
  112. openbrowse-1.8.1/tests/test_schema.py +240 -0
  113. openbrowse-1.8.1/tests/test_service.py +127 -0
  114. openbrowse-1.8.1/tests/test_sessions_api.py +637 -0
  115. openbrowse-1.8.1/tests/test_setup.py +212 -0
  116. openbrowse-1.8.1/tests/test_textguard.py +26 -0
  117. openbrowse-1.8.1/tests/test_tools.py +3615 -0
  118. openbrowse-1.8.1/tests/test_updates.py +296 -0
  119. openbrowse-1.8.1/tests/test_version.py +181 -0
@@ -0,0 +1,37 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+ *.egg-info/
6
+ dist/
7
+ build/
8
+ *.egg
9
+
10
+ # Virtual environment
11
+ .venv/
12
+ venv/
13
+ ENV/
14
+
15
+ # Environment variables
16
+ .env
17
+
18
+ # Data directory (keep .gitkeep)
19
+ data/*
20
+ !data/.gitkeep
21
+
22
+ # IDE
23
+ .idea/
24
+ .vscode/
25
+ *.swp
26
+ *.swo
27
+
28
+ # OS
29
+ .DS_Store
30
+ Thumbs.db
31
+ .worktrees/
32
+ .claude
33
+ .playwright-mcp/
34
+ uv.lock
35
+
36
+ # Internal planning notes, never tracked
37
+ docs/
@@ -0,0 +1,23 @@
1
+ cff-version: 1.2.0
2
+ message: "If you use OpenBrowse in your research or project, please cite it as below."
3
+ type: software
4
+ title: "OpenBrowse: Self-hosted AI browser agents"
5
+ version: 1.8.1
6
+ date-released: 2026-08-20
7
+ license: MIT
8
+ doi: "10.5281/zenodo.21986248"
9
+ authors:
10
+ - family-names: "Johnston Kurilov"
11
+ given-names: "Lucas"
12
+ orcid: "https://orcid.org/0009-0004-3743-2349"
13
+ repository-code: "https://github.com/lujstn/openbrowse"
14
+ url: "https://openbrowse.co"
15
+ abstract: "OpenBrowse is an open-source, self-hosted alternative to Browser Use Cloud: AI browser agents that run on your own hardware, driven through the v3 REST API, with schema-validated structured output, anti-hallucination grounding guards, and a live visual dashboard."
16
+ keywords:
17
+ - artificial intelligence
18
+ - ai agents
19
+ - browser automation
20
+ - browser agents
21
+ - structured data extraction
22
+ - web scraping
23
+ - self-hosted
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Lucas Johnston Kurilov
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.
@@ -0,0 +1,254 @@
1
+ Metadata-Version: 2.5
2
+ Name: openbrowse
3
+ Version: 1.8.1
4
+ Summary: Self-hosted AI browser automation server with a live dashboard, built for small machines like the Raspberry Pi
5
+ Project-URL: Homepage, https://openbrowse.co
6
+ Project-URL: Repository, https://github.com/lujstn/openbrowse
7
+ Project-URL: Issues, https://github.com/lujstn/openbrowse/issues
8
+ Author: Lucas Johnston Kurilov
9
+ License: MIT License
10
+
11
+ Copyright (c) 2026 Lucas Johnston Kurilov
12
+
13
+ Permission is hereby granted, free of charge, to any person obtaining a copy
14
+ of this software and associated documentation files (the "Software"), to deal
15
+ in the Software without restriction, including without limitation the rights
16
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
17
+ copies of the Software, and to permit persons to whom the Software is
18
+ furnished to do so, subject to the following conditions:
19
+
20
+ The above copyright notice and this permission notice shall be included in all
21
+ copies or substantial portions of the Software.
22
+
23
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
29
+ SOFTWARE.
30
+ License-File: LICENSE
31
+ Keywords: ai-agent,automation,browser-use,raspberry-pi,self-hosted
32
+ Classifier: Development Status :: 5 - Production/Stable
33
+ Classifier: Environment :: Web Environment
34
+ Classifier: Framework :: FastAPI
35
+ Classifier: Intended Audience :: Developers
36
+ Classifier: Operating System :: POSIX :: Linux
37
+ Classifier: Programming Language :: Python :: 3.11
38
+ Classifier: Programming Language :: Python :: 3.12
39
+ Classifier: Topic :: Internet :: WWW/HTTP
40
+ Requires-Python: >=3.11
41
+ Requires-Dist: aiosqlite>=0.20.0
42
+ Requires-Dist: browser-use<0.14,>=0.13.7
43
+ Requires-Dist: cloakbrowser>=0.3.0
44
+ Requires-Dist: fastapi>=0.115.0
45
+ Requires-Dist: httpx>=0.28.0
46
+ Requires-Dist: jinja2>=3.1.0
47
+ Requires-Dist: pydantic>=2.0.0
48
+ Requires-Dist: python-dotenv>=1.0.0
49
+ Requires-Dist: python-multipart>=0.0.9
50
+ Requires-Dist: sse-starlette>=2.0.0
51
+ Requires-Dist: uvicorn[standard]>=0.34.0
52
+ Requires-Dist: websockets>=12.0
53
+ Provides-Extra: dev
54
+ Requires-Dist: httpx; extra == 'dev'
55
+ Requires-Dist: pytest; extra == 'dev'
56
+ Requires-Dist: pytest-asyncio; extra == 'dev'
57
+ Description-Content-Type: text/markdown
58
+
59
+ <p align="center">
60
+ <picture>
61
+ <source media="(prefers-color-scheme: dark)" srcset=".github/assets/openbrowse-dark.gif">
62
+ <img src=".github/assets/openbrowse-light.gif" alt="OpenBrowse" width="432">
63
+ </picture>
64
+ </p>
65
+
66
+ # OpenBrowse
67
+
68
+ **The open-source Browser Use Cloud alternative.** Self-host AI browser agents on a Raspberry Pi or any VPS, drive them through the same v3 REST API the `browser-use-sdk` already speaks, and watch every run live in a real browser. Built on top of the [Browser Use](https://github.com/browser-use/browser-use) SDK. It's cheaper, faster, and more reliable than BU Cloud.
69
+
70
+ [openbrowse.co](https://openbrowse.co)
71
+
72
+ <div align="left">
73
+ <a href="https://buildin.london"><img src="https://buildin.london/badge.svg" alt="Built in London" style="width: 200px;"></a>
74
+ </div>
75
+
76
+ ---
77
+
78
+ ## Benchmarks
79
+
80
+ Given the same real-world extraction task (a careers page with 14 records behind an embedded, cross-origin board, requiring full schema output):
81
+
82
+ | Runtime | Model | Reasoning | Time | Tokens | LLM cost | Records |
83
+ | --- | --- | --- | ---: | ---: | ---: | --- |
84
+ | BU Cloud | claude-sonnet-5 | high | 2m 36s | 859k | $0.78 | 14/14<sup>1</sup> |
85
+ | **OpenBrowse** | **gpt-5.6-terra** | **none** | **1m 47s** | **202k** | **$0.24** | **14/14** |
86
+ | **OpenBrowse** | **claude-sonnet-5** | **high** | 4m 02s | **242k** | **$0.40** | **14/14** |
87
+
88
+ <sub><i><sup>1</sup> Extracted successfully, though some returned fields (e.g. job seniority) were hallucinated when not shown on the page.</i></sub>
89
+
90
+ ## Why OpenBrowse over BU Cloud?
91
+
92
+ | | BU Cloud | OpenBrowse |
93
+ | --- | --- | --- |
94
+ | Hosting | Managed, per-task pricing | Your hardware, you pay only LLM tokens |
95
+ | How it works | Code-first: the agent scripts its way through pages | Visual-first: the agent opens real tabs you can watch live, like a human working |
96
+ | Bulk page reads | One page at a time | `read_pages` opens whole listings in parallel tab waves, one step |
97
+ | Structured output | Schema-validated | Schema-validated, plus a live answer store with a completeness gate the agent must pass before finishing |
98
+ | Anti-hallucination | Often fills fields the page never shows | On-screen data first, enriched from the page's own structured data (JSON-LD, APIs), never guessed: values without evidence are refused at the store boundary |
99
+ | Profiles | Cloud profiles | Import your BU Cloud profiles (cookies and localStorage) with one command |
100
+ | Live view | Replay | Real-time VNC of the actual browser, a step feed with the model's reasoning, and an IDE-style code tab that streams the agent's sandbox scripts live as they're written |
101
+ | API | v3 REST | The same v3 REST surface: point `browser-use-sdk` at your box and change nothing but `baseUrl` and `apiKey` |
102
+
103
+ ### ⚡ See it in action
104
+
105
+ Here's a snippet of our benchmark run, with `claude-opus-5` taking agentic actions across parallel tabs while its code streams into a live IDE.
106
+
107
+ <https://github.com/user-attachments/assets/c1330d77-67b6-4a7d-bd43-7cdfa230b9d1>
108
+
109
+ ## Model providers
110
+
111
+ ### Recommended models
112
+
113
+ 1. **For most use cases**, `gpt-5.6-terra { "reasoningEffort": "none" }`, `gpt-5.6-sol { "reasoningEffort": "none" }` and `claude-sonnet-5 { "reasoningEffort": "high" }` all strike a great balance of reliability, accuracy, and cost.
114
+
115
+ 2. **For intense workflows**, use `claude-opus-5 { "reasoningEffort": "medium" }` or `gpt-5.6-sol { "reasoningEffort": "none" }` — both are great options, but watch out for token burn.
116
+
117
+ 3. **On a budget?** Use `gpt-5.6-luna { "reasoningEffort": "max" }` with a tightly focused prompt. It might take a while, and it's more prone to hallucinations (especially with broad prompts), but the actual extractions are still great quality.
118
+
119
+ ### Comparisons
120
+
121
+ The same real-world extraction task (a careers page with 14 records behind an embedded, cross-origin board, full schema output) run against BU Cloud and against OpenBrowse on a Raspberry Pi 5 (16GB) without concurrency, ordered cheapest to most expensive:
122
+
123
+ | Runtime | Model | Reasoning | Steps | Time | Tokens | LLM cost | Records |
124
+ | --- | --- | --- | --- | ---: | ---: | ---: | --- |
125
+ | OpenBrowse | **gpt-5.6-luna** | **max** | 36 | 17m 03s | 1.08M | **$0.22** | 14/14 |
126
+ | OpenBrowse | **gpt-5.6-terra** | **none** | 11 | **1m 47s** | **202k** | **$0.24** | 14/14 |
127
+ | OpenBrowse | **claude-sonnet-5** | **high** | 10 | 4m 02s | **242k** | **$0.40** | 14/14 |
128
+ | OpenBrowse | **gpt-5.6-sol** | **none** | **8** | **2m 03s** | **136k** | **$0.41** | 14/14 |
129
+ | OpenBrowse | **claude-sonnet-5** | **none** | **9** | 5m 18s | **237k** | **$0.51** | 14/14 |
130
+ | OpenBrowse | **gpt-5.6-terra** | **high** | 17 | 5m 05s | **434k** | **$0.66** | 14/14 |
131
+ | BU Cloud | claude-sonnet-5 | high | 10 | 2m 36s | 859k | $0.78 | 14/14<sup>1</sup> |
132
+ | OpenBrowse | gpt-5.6-sol | medium | 16 | 5m 24s | **339k** | $1.12 | 14/14 |
133
+ | OpenBrowse | claude-opus-5 | medium | 15 | 3m 56s | **398k** | $1.32 | 14/14 |
134
+ | OpenBrowse | claude-opus-5 | none | 17 | 4m 53s | **480k** | $1.62 | 14/14 |
135
+
136
+ <sub><i><sup>1</sup> Extracted successfully, though some returned fields (e.g. job seniority) were hallucinated when not shown on the page.</i></sub>
137
+
138
+ OpenAI and Anthropic models are generally at their best at the opposite ends of the reasoning dial. For example, OpenAI's GPT-5.6-Terra performs better with less reasoning, spending less time planning ahead and more time reacting to the page in front of it, while Anthropic's 5-series Claude models lean towards rabbit holes and need reasoning time to refocus on the goal.
139
+
140
+ ### What is "thinking" in OpenBrowse?
141
+
142
+ OpenBrowse separates two kinds of reasoning. **Browser thinking** is our way of describing how the platform works in "steps" (the 👁️ see / 🛝 plan / ➡️ next / 💭 thinking cards in the live feed), so it can't be disabled.
143
+
144
+ **Model reasoning** is different: it's the Chain-of-Thought reasoning provided by LLM providers (e.g. Anthropic's extended thinking, OpenAI's reasoning effort), and can be controlled per session by changing `reasoningEffort` in the API. Values are validated per model at runtime. Models will have different default reasoning levels depending on their provider, so it's a good idea to set this value explicitly.
145
+
146
+ ### All supported models
147
+
148
+ - OpenAI: `gpt-5.6-sol`, `gpt-5.6-terra`, `gpt-5.6-luna`
149
+ - Anthropic: `claude-mythos-5`, `claude-fable-5`, `claude-opus-5`, `claude-sonnet-5`, `claude-opus-4.8`, `claude-opus-4.8[1m]`, `claude-opus-4.7`, `claude-opus-4.7[1m]`, `claude-opus-4.6`, `claude-opus-4.6[1m]`, `claude-sonnet-4.6`, `claude-sonnet-4.6[1m]`
150
+ - Google: ⚠️ Coming soon
151
+
152
+ ## Quick start
153
+
154
+ ```bash
155
+ curl -LsSf https://astral.sh/uv/install.sh | sh
156
+ uv tool install openbrowse
157
+ openbrowse start
158
+ ```
159
+
160
+ `openbrowse start` registers OpenBrowse as a systemd service, so it is running now **and starts automatically on every boot from here on** (`openbrowse stop --disable` undoes that). On a machine without systemd it falls back to running in the foreground, as does `openbrowse serve` anywhere.
161
+
162
+ To run from source instead, clone the repo and use uv directly:
163
+
164
+ ```bash
165
+ git clone git@github.com:lujstn/openbrowse.git
166
+ cd openbrowse
167
+ uv sync
168
+ uv run openbrowse serve
169
+ ```
170
+
171
+ Open `http://<your-host>:8420` in a browser. A fresh install serves a one-time **setup screen** that generates your API bearer key, takes your Anthropic / OpenAI / CapSolver keys, sets your dashboard password and concurrency limit, and writes `.env` for you.
172
+
173
+ Then from any `browser-use-sdk` client:
174
+
175
+ ```ts
176
+ import { BrowserUse } from "browser-use-sdk";
177
+
178
+ const client = new BrowserUse({
179
+ apiKey: process.env.OPENBROWSE_API_KEY,
180
+ baseUrl: "http://<your-host>:8420/v3",
181
+ });
182
+
183
+ const task = await client.tasks.create({
184
+ task: "Find every product on this page and return the structured list.",
185
+ model: "claude-sonnet-5",
186
+ outputSchema: mySchema,
187
+ });
188
+ ```
189
+
190
+ Full installation (Raspberry Pi system packages, Xvfb + VNC live view, systemd service): see [GETTING_STARTED.md](GETTING_STARTED.md).
191
+
192
+ ## Updating
193
+
194
+ The server checks PyPI for new releases in the background and shows an **Update available** badge in the dashboard when one exists; installing it is one click on the Settings page (the server restarts itself afterwards). From a shell, `openbrowse check-update` and `openbrowse update` do the same. Set `UPDATE_CHECK_HOURS=0` in `.env` to disable the background check.
195
+
196
+ ## Exposing it to the web
197
+
198
+ OpenBrowse runs on plain Python + Chromium and can be easily port forwarded. To reach it from outside that box without opening ports, put it behind [Tailscale](https://tailscale.com/):
199
+
200
+ ```bash
201
+ # private access from your own devices
202
+ tailscale up
203
+
204
+ # or expose the API publicly over TLS with Tailscale Funnel
205
+ sudo tailscale funnel --bg 8420
206
+ ```
207
+
208
+ ## Features
209
+
210
+ - **v3-compatible REST API**: sessions, structured output schemas, cost caps, live URLs.
211
+ - **Visual, tab-based browsing**: parallel foreground tab waves for bulk reads; a code tab shows when the agent runs a script; everything visible over VNC.
212
+ - **Schema answer store**: every write validated live against your JSON Schema, coverage tracked per field, a completeness gate before `done`, and mark-absent semantics for data a site genuinely does not publish.
213
+ - **Grounding guards**: shell-read detection with automatic in-frame retry, evidence-checked enum writes, URL fields validated as absolute http(s) links at the store boundary, honest failure over invented data.
214
+ - **Profile import**: bring BU Cloud profiles (cookies + localStorage) via CLI or the dashboard.
215
+ - **Dashboard**: live session feed with model reasoning, per-step costs, JSON export (full / steps / output-only), profile management.
216
+ - **CAPTCHA solving**: optional CapSolver integration.
217
+ - **Multi-provider**: Anthropic and OpenAI models behind one API, with per-provider repair layers for each family's failure modes.
218
+
219
+ ## Citation
220
+
221
+ This project is licensed under the MIT License. If you use OpenBrowse as part of your research or project, please cite:
222
+
223
+ ```bibtex
224
+ @software{openbrowse2026,
225
+ author = {Johnston Kurilov, Lucas},
226
+ title = {{OpenBrowse}: Self-hosted AI browser agents},
227
+ year = {2026},
228
+ publisher = {Zenodo},
229
+ doi = {10.5281/zenodo.21986248},
230
+ url = {https://github.com/lujstn/openbrowse}
231
+ }
232
+ ```
233
+
234
+ [![DOI](https://zenodo.org/badge/1210478161.svg)](https://doi.org/10.5281/zenodo.21986248)
235
+
236
+ <br><br>
237
+
238
+ <table align="center">
239
+ <tr>
240
+ <th colspan="2">built with <з by @lujstn</th>
241
+ </tr>
242
+ <tr>
243
+ <td><img src=".github/assets/IMG_8874.jpg" alt="@lujstn" width="400"></td>
244
+ <td valign="middle">
245
+ <a href="https://x.com/intent/user?screen_name=lujstn"><img src="https://img.shields.io/twitter/follow/lujstn?style=social" alt="Twitter"></a>
246
+ <br>
247
+ <a href="https://www.instagram.com/lujstn/"><img src="https://img.shields.io/badge/Instagram-Follow-E4405F?style=social&logo=instagram" alt="Instagram"></a>
248
+ <br>
249
+ <a href="https://www.tiktok.com/@lujstn"><img src="https://img.shields.io/badge/TikTok-000000?style=flat&logo=tiktok&logoColor=white" alt="TikTok"></a>
250
+ <br>
251
+ <a href="https://lujstn.com"><img src="https://img.shields.io/badge/%F0%9F%94%97_lujstn.com-1a1a1a" alt="lujstn.com"></a>
252
+ </td>
253
+ </tr>
254
+ </table>
@@ -0,0 +1,196 @@
1
+ <p align="center">
2
+ <picture>
3
+ <source media="(prefers-color-scheme: dark)" srcset=".github/assets/openbrowse-dark.gif">
4
+ <img src=".github/assets/openbrowse-light.gif" alt="OpenBrowse" width="432">
5
+ </picture>
6
+ </p>
7
+
8
+ # OpenBrowse
9
+
10
+ **The open-source Browser Use Cloud alternative.** Self-host AI browser agents on a Raspberry Pi or any VPS, drive them through the same v3 REST API the `browser-use-sdk` already speaks, and watch every run live in a real browser. Built on top of the [Browser Use](https://github.com/browser-use/browser-use) SDK. It's cheaper, faster, and more reliable than BU Cloud.
11
+
12
+ [openbrowse.co](https://openbrowse.co)
13
+
14
+ <div align="left">
15
+ <a href="https://buildin.london"><img src="https://buildin.london/badge.svg" alt="Built in London" style="width: 200px;"></a>
16
+ </div>
17
+
18
+ ---
19
+
20
+ ## Benchmarks
21
+
22
+ Given the same real-world extraction task (a careers page with 14 records behind an embedded, cross-origin board, requiring full schema output):
23
+
24
+ | Runtime | Model | Reasoning | Time | Tokens | LLM cost | Records |
25
+ | --- | --- | --- | ---: | ---: | ---: | --- |
26
+ | BU Cloud | claude-sonnet-5 | high | 2m 36s | 859k | $0.78 | 14/14<sup>1</sup> |
27
+ | **OpenBrowse** | **gpt-5.6-terra** | **none** | **1m 47s** | **202k** | **$0.24** | **14/14** |
28
+ | **OpenBrowse** | **claude-sonnet-5** | **high** | 4m 02s | **242k** | **$0.40** | **14/14** |
29
+
30
+ <sub><i><sup>1</sup> Extracted successfully, though some returned fields (e.g. job seniority) were hallucinated when not shown on the page.</i></sub>
31
+
32
+ ## Why OpenBrowse over BU Cloud?
33
+
34
+ | | BU Cloud | OpenBrowse |
35
+ | --- | --- | --- |
36
+ | Hosting | Managed, per-task pricing | Your hardware, you pay only LLM tokens |
37
+ | How it works | Code-first: the agent scripts its way through pages | Visual-first: the agent opens real tabs you can watch live, like a human working |
38
+ | Bulk page reads | One page at a time | `read_pages` opens whole listings in parallel tab waves, one step |
39
+ | Structured output | Schema-validated | Schema-validated, plus a live answer store with a completeness gate the agent must pass before finishing |
40
+ | Anti-hallucination | Often fills fields the page never shows | On-screen data first, enriched from the page's own structured data (JSON-LD, APIs), never guessed: values without evidence are refused at the store boundary |
41
+ | Profiles | Cloud profiles | Import your BU Cloud profiles (cookies and localStorage) with one command |
42
+ | Live view | Replay | Real-time VNC of the actual browser, a step feed with the model's reasoning, and an IDE-style code tab that streams the agent's sandbox scripts live as they're written |
43
+ | API | v3 REST | The same v3 REST surface: point `browser-use-sdk` at your box and change nothing but `baseUrl` and `apiKey` |
44
+
45
+ ### ⚡ See it in action
46
+
47
+ Here's a snippet of our benchmark run, with `claude-opus-5` taking agentic actions across parallel tabs while its code streams into a live IDE.
48
+
49
+ <https://github.com/user-attachments/assets/c1330d77-67b6-4a7d-bd43-7cdfa230b9d1>
50
+
51
+ ## Model providers
52
+
53
+ ### Recommended models
54
+
55
+ 1. **For most use cases**, `gpt-5.6-terra { "reasoningEffort": "none" }`, `gpt-5.6-sol { "reasoningEffort": "none" }` and `claude-sonnet-5 { "reasoningEffort": "high" }` all strike a great balance of reliability, accuracy, and cost.
56
+
57
+ 2. **For intense workflows**, use `claude-opus-5 { "reasoningEffort": "medium" }` or `gpt-5.6-sol { "reasoningEffort": "none" }` — both are great options, but watch out for token burn.
58
+
59
+ 3. **On a budget?** Use `gpt-5.6-luna { "reasoningEffort": "max" }` with a tightly focused prompt. It might take a while, and it's more prone to hallucinations (especially with broad prompts), but the actual extractions are still great quality.
60
+
61
+ ### Comparisons
62
+
63
+ The same real-world extraction task (a careers page with 14 records behind an embedded, cross-origin board, full schema output) run against BU Cloud and against OpenBrowse on a Raspberry Pi 5 (16GB) without concurrency, ordered cheapest to most expensive:
64
+
65
+ | Runtime | Model | Reasoning | Steps | Time | Tokens | LLM cost | Records |
66
+ | --- | --- | --- | --- | ---: | ---: | ---: | --- |
67
+ | OpenBrowse | **gpt-5.6-luna** | **max** | 36 | 17m 03s | 1.08M | **$0.22** | 14/14 |
68
+ | OpenBrowse | **gpt-5.6-terra** | **none** | 11 | **1m 47s** | **202k** | **$0.24** | 14/14 |
69
+ | OpenBrowse | **claude-sonnet-5** | **high** | 10 | 4m 02s | **242k** | **$0.40** | 14/14 |
70
+ | OpenBrowse | **gpt-5.6-sol** | **none** | **8** | **2m 03s** | **136k** | **$0.41** | 14/14 |
71
+ | OpenBrowse | **claude-sonnet-5** | **none** | **9** | 5m 18s | **237k** | **$0.51** | 14/14 |
72
+ | OpenBrowse | **gpt-5.6-terra** | **high** | 17 | 5m 05s | **434k** | **$0.66** | 14/14 |
73
+ | BU Cloud | claude-sonnet-5 | high | 10 | 2m 36s | 859k | $0.78 | 14/14<sup>1</sup> |
74
+ | OpenBrowse | gpt-5.6-sol | medium | 16 | 5m 24s | **339k** | $1.12 | 14/14 |
75
+ | OpenBrowse | claude-opus-5 | medium | 15 | 3m 56s | **398k** | $1.32 | 14/14 |
76
+ | OpenBrowse | claude-opus-5 | none | 17 | 4m 53s | **480k** | $1.62 | 14/14 |
77
+
78
+ <sub><i><sup>1</sup> Extracted successfully, though some returned fields (e.g. job seniority) were hallucinated when not shown on the page.</i></sub>
79
+
80
+ OpenAI and Anthropic models are generally at their best at the opposite ends of the reasoning dial. For example, OpenAI's GPT-5.6-Terra performs better with less reasoning, spending less time planning ahead and more time reacting to the page in front of it, while Anthropic's 5-series Claude models lean towards rabbit holes and need reasoning time to refocus on the goal.
81
+
82
+ ### What is "thinking" in OpenBrowse?
83
+
84
+ OpenBrowse separates two kinds of reasoning. **Browser thinking** is our way of describing how the platform works in "steps" (the 👁️ see / 🛝 plan / ➡️ next / 💭 thinking cards in the live feed), so it can't be disabled.
85
+
86
+ **Model reasoning** is different: it's the Chain-of-Thought reasoning provided by LLM providers (e.g. Anthropic's extended thinking, OpenAI's reasoning effort), and can be controlled per session by changing `reasoningEffort` in the API. Values are validated per model at runtime. Models will have different default reasoning levels depending on their provider, so it's a good idea to set this value explicitly.
87
+
88
+ ### All supported models
89
+
90
+ - OpenAI: `gpt-5.6-sol`, `gpt-5.6-terra`, `gpt-5.6-luna`
91
+ - Anthropic: `claude-mythos-5`, `claude-fable-5`, `claude-opus-5`, `claude-sonnet-5`, `claude-opus-4.8`, `claude-opus-4.8[1m]`, `claude-opus-4.7`, `claude-opus-4.7[1m]`, `claude-opus-4.6`, `claude-opus-4.6[1m]`, `claude-sonnet-4.6`, `claude-sonnet-4.6[1m]`
92
+ - Google: ⚠️ Coming soon
93
+
94
+ ## Quick start
95
+
96
+ ```bash
97
+ curl -LsSf https://astral.sh/uv/install.sh | sh
98
+ uv tool install openbrowse
99
+ openbrowse start
100
+ ```
101
+
102
+ `openbrowse start` registers OpenBrowse as a systemd service, so it is running now **and starts automatically on every boot from here on** (`openbrowse stop --disable` undoes that). On a machine without systemd it falls back to running in the foreground, as does `openbrowse serve` anywhere.
103
+
104
+ To run from source instead, clone the repo and use uv directly:
105
+
106
+ ```bash
107
+ git clone git@github.com:lujstn/openbrowse.git
108
+ cd openbrowse
109
+ uv sync
110
+ uv run openbrowse serve
111
+ ```
112
+
113
+ Open `http://<your-host>:8420` in a browser. A fresh install serves a one-time **setup screen** that generates your API bearer key, takes your Anthropic / OpenAI / CapSolver keys, sets your dashboard password and concurrency limit, and writes `.env` for you.
114
+
115
+ Then from any `browser-use-sdk` client:
116
+
117
+ ```ts
118
+ import { BrowserUse } from "browser-use-sdk";
119
+
120
+ const client = new BrowserUse({
121
+ apiKey: process.env.OPENBROWSE_API_KEY,
122
+ baseUrl: "http://<your-host>:8420/v3",
123
+ });
124
+
125
+ const task = await client.tasks.create({
126
+ task: "Find every product on this page and return the structured list.",
127
+ model: "claude-sonnet-5",
128
+ outputSchema: mySchema,
129
+ });
130
+ ```
131
+
132
+ Full installation (Raspberry Pi system packages, Xvfb + VNC live view, systemd service): see [GETTING_STARTED.md](GETTING_STARTED.md).
133
+
134
+ ## Updating
135
+
136
+ The server checks PyPI for new releases in the background and shows an **Update available** badge in the dashboard when one exists; installing it is one click on the Settings page (the server restarts itself afterwards). From a shell, `openbrowse check-update` and `openbrowse update` do the same. Set `UPDATE_CHECK_HOURS=0` in `.env` to disable the background check.
137
+
138
+ ## Exposing it to the web
139
+
140
+ OpenBrowse runs on plain Python + Chromium and can be easily port forwarded. To reach it from outside that box without opening ports, put it behind [Tailscale](https://tailscale.com/):
141
+
142
+ ```bash
143
+ # private access from your own devices
144
+ tailscale up
145
+
146
+ # or expose the API publicly over TLS with Tailscale Funnel
147
+ sudo tailscale funnel --bg 8420
148
+ ```
149
+
150
+ ## Features
151
+
152
+ - **v3-compatible REST API**: sessions, structured output schemas, cost caps, live URLs.
153
+ - **Visual, tab-based browsing**: parallel foreground tab waves for bulk reads; a code tab shows when the agent runs a script; everything visible over VNC.
154
+ - **Schema answer store**: every write validated live against your JSON Schema, coverage tracked per field, a completeness gate before `done`, and mark-absent semantics for data a site genuinely does not publish.
155
+ - **Grounding guards**: shell-read detection with automatic in-frame retry, evidence-checked enum writes, URL fields validated as absolute http(s) links at the store boundary, honest failure over invented data.
156
+ - **Profile import**: bring BU Cloud profiles (cookies + localStorage) via CLI or the dashboard.
157
+ - **Dashboard**: live session feed with model reasoning, per-step costs, JSON export (full / steps / output-only), profile management.
158
+ - **CAPTCHA solving**: optional CapSolver integration.
159
+ - **Multi-provider**: Anthropic and OpenAI models behind one API, with per-provider repair layers for each family's failure modes.
160
+
161
+ ## Citation
162
+
163
+ This project is licensed under the MIT License. If you use OpenBrowse as part of your research or project, please cite:
164
+
165
+ ```bibtex
166
+ @software{openbrowse2026,
167
+ author = {Johnston Kurilov, Lucas},
168
+ title = {{OpenBrowse}: Self-hosted AI browser agents},
169
+ year = {2026},
170
+ publisher = {Zenodo},
171
+ doi = {10.5281/zenodo.21986248},
172
+ url = {https://github.com/lujstn/openbrowse}
173
+ }
174
+ ```
175
+
176
+ [![DOI](https://zenodo.org/badge/1210478161.svg)](https://doi.org/10.5281/zenodo.21986248)
177
+
178
+ <br><br>
179
+
180
+ <table align="center">
181
+ <tr>
182
+ <th colspan="2">built with <з by @lujstn</th>
183
+ </tr>
184
+ <tr>
185
+ <td><img src=".github/assets/IMG_8874.jpg" alt="@lujstn" width="400"></td>
186
+ <td valign="middle">
187
+ <a href="https://x.com/intent/user?screen_name=lujstn"><img src="https://img.shields.io/twitter/follow/lujstn?style=social" alt="Twitter"></a>
188
+ <br>
189
+ <a href="https://www.instagram.com/lujstn/"><img src="https://img.shields.io/badge/Instagram-Follow-E4405F?style=social&logo=instagram" alt="Instagram"></a>
190
+ <br>
191
+ <a href="https://www.tiktok.com/@lujstn"><img src="https://img.shields.io/badge/TikTok-000000?style=flat&logo=tiktok&logoColor=white" alt="TikTok"></a>
192
+ <br>
193
+ <a href="https://lujstn.com"><img src="https://img.shields.io/badge/%F0%9F%94%97_lujstn.com-1a1a1a" alt="lujstn.com"></a>
194
+ </td>
195
+ </tr>
196
+ </table>
@@ -0,0 +1,26 @@
1
+ """OpenBrowse: self-hosted AI browser automation with a live dashboard."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import re
6
+ from importlib import metadata
7
+ from pathlib import Path
8
+
9
+
10
+ def _read_version() -> str:
11
+ # @nonobvious(must-hold): a git checkout's pyproject.toml outranks installed
12
+ # metadata, which goes stale between a version bump and the next `uv sync`.
13
+ pyproject = Path(__file__).resolve().parent.parent / "pyproject.toml"
14
+ try:
15
+ match = re.search(r'^version = "([^"]+)"$', pyproject.read_text(), re.MULTILINE)
16
+ if match:
17
+ return match.group(1)
18
+ except OSError:
19
+ pass
20
+ try:
21
+ return metadata.version("openbrowse")
22
+ except metadata.PackageNotFoundError:
23
+ return "0.0.0"
24
+
25
+
26
+ __version__ = _read_version()
File without changes
@@ -0,0 +1,96 @@
1
+ """Ephemeral per-session activity state for the live feed.
2
+
3
+ Holds what a running session is doing *right now* (waiting for the model, acting,
4
+ preparing the next step) plus when that phase started, so the dashboard can show a
5
+ live indicator with a count-up timer. Also the run-lifecycle registry: which
6
+ sessions are currently running and which profile each has claimed, letting tools
7
+ scale contention pacing to real concurrency and letting the runner refuse two
8
+ live sessions on one profile. In-memory only; never persisted.
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ from datetime import datetime, timezone
14
+
15
+
16
+ _activity: dict[str, dict] = {}
17
+
18
+
19
+ def set_activity(
20
+ session_id: str,
21
+ label: str,
22
+ step: int | None = None,
23
+ spin: bool = False,
24
+ stream: str | None = None,
25
+ seconds: float | None = None,
26
+ kind: str | None = None,
27
+ ) -> None:
28
+ """Record what a session is doing. ``stream`` carries the full accumulated
29
+ text of a token-by-token phase (model reasoning as it generates); it is
30
+ never a window or a tail slice, and a call that omits it clears any
31
+ previous stream rather than inheriting it, so a phase change (e.g. moving
32
+ to "Running actions") can't leak stale reasoning text into the next read.
33
+ """
34
+ prev = _activity.get(session_id) or {}
35
+ now = datetime.now(timezone.utc).isoformat()
36
+ # @nonobvious(must-hold): a streaming phase re-pushes several times a second,
37
+ # so the clock has to survive an unchanged label or it reads near zero forever
38
+ # and no phase can report how long it took.
39
+ started = prev.get("startedAt") if prev.get("label") == label else None
40
+ _activity[session_id] = {
41
+ "label": label,
42
+ "startedAt": started or now,
43
+ "step": step if step is not None else prev.get("step"),
44
+ "spin": spin,
45
+ "stream": stream,
46
+ # @nonobvious(forced-by): a phase whose label was interrupted and restored
47
+ # loses its clock, so a caller that measured the real elapsed time says so
48
+ # here rather than leaving the dashboard to infer it from startedAt.
49
+ "seconds": seconds,
50
+ # @nonobvious(means): "reasoning" marks a phase whose prose is the point.
51
+ # The dashboard shimmers those and spins the rest, so a phase that thinks
52
+ # and a phase that acts do not claim the same affordance.
53
+ "kind": kind,
54
+ }
55
+
56
+
57
+ def get_activity(session_id: str) -> dict | None:
58
+ return _activity.get(session_id)
59
+
60
+
61
+ def clear_activity(session_id: str) -> None:
62
+ _activity.pop(session_id, None)
63
+
64
+
65
+ _running_sessions: set[str] = set()
66
+
67
+ _claimed_profiles: dict[str, str] = {}
68
+
69
+
70
+ def session_started(session_id: str) -> None:
71
+ _running_sessions.add(session_id)
72
+
73
+
74
+ def session_ended(session_id: str) -> None:
75
+ _running_sessions.discard(session_id)
76
+
77
+
78
+ def active_session_count() -> int:
79
+ return len(_running_sessions)
80
+
81
+
82
+ def try_claim_profile(profile_id: str, session_id: str) -> str | None:
83
+ """Claim a profile for a session. Returns None on success, or the id of the
84
+ session already holding it. Check-and-set with no await between, so a single
85
+ event loop cannot interleave two claims.
86
+ """
87
+ holder = _claimed_profiles.get(profile_id)
88
+ if holder is not None and holder != session_id:
89
+ return holder
90
+ _claimed_profiles[profile_id] = session_id
91
+ return None
92
+
93
+
94
+ def release_profile(profile_id: str, session_id: str) -> None:
95
+ if _claimed_profiles.get(profile_id) == session_id:
96
+ _claimed_profiles.pop(profile_id, None)