persistent-browser-bridge 0.1.0__py3-none-any.whl
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.
- pbb/__init__.py +5 -0
- pbb/browser/__init__.py +1 -0
- pbb/browser/browser_detection.py +117 -0
- pbb/browser/locator.py +103 -0
- pbb/browser/locks.py +77 -0
- pbb/browser/manager.py +218 -0
- pbb/browser/profile.py +56 -0
- pbb/browser/snapshot.py +139 -0
- pbb/cli.py +305 -0
- pbb/config.py +66 -0
- pbb/constants.py +7 -0
- pbb/daemon/__init__.py +1 -0
- pbb/daemon/app.py +137 -0
- pbb/daemon/client.py +39 -0
- pbb/daemon/server.py +36 -0
- pbb/utils/__init__.py +1 -0
- pbb/utils/paths.py +40 -0
- persistent_browser_bridge-0.1.0.dist-info/METADATA +210 -0
- persistent_browser_bridge-0.1.0.dist-info/RECORD +22 -0
- persistent_browser_bridge-0.1.0.dist-info/WHEEL +4 -0
- persistent_browser_bridge-0.1.0.dist-info/entry_points.txt +2 -0
- persistent_browser_bridge-0.1.0.dist-info/licenses/LICENSE +22 -0
pbb/daemon/client.py
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"""Synchronous local daemon client used by the CLI."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
import httpx
|
|
8
|
+
|
|
9
|
+
from pbb.config import Settings
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class DaemonUnavailable(RuntimeError):
|
|
13
|
+
pass
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class DaemonClient:
|
|
17
|
+
def __init__(self, settings: Settings) -> None:
|
|
18
|
+
self.base_url = f"http://{settings.daemon_host}:{settings.daemon_port}"
|
|
19
|
+
self.timeout = max(settings.timeout / 1000 + 5, 10)
|
|
20
|
+
|
|
21
|
+
def request(
|
|
22
|
+
self, method: str, path: str, payload: dict[str, Any] | None = None
|
|
23
|
+
) -> dict[str, Any]:
|
|
24
|
+
try:
|
|
25
|
+
with httpx.Client(timeout=self.timeout, trust_env=False) as client:
|
|
26
|
+
response = client.request(method, f"{self.base_url}{path}", json=payload)
|
|
27
|
+
except httpx.RequestError as exc:
|
|
28
|
+
raise DaemonUnavailable("PBB daemon is not running. Run `pbb start` first.") from exc
|
|
29
|
+
try:
|
|
30
|
+
data = response.json()
|
|
31
|
+
except ValueError as exc:
|
|
32
|
+
raise DaemonUnavailable("PBB daemon returned an invalid response") from exc
|
|
33
|
+
return data
|
|
34
|
+
|
|
35
|
+
def status(self) -> dict[str, Any]:
|
|
36
|
+
return self.request("GET", "/status")
|
|
37
|
+
|
|
38
|
+
def post(self, path: str, payload: dict[str, Any] | None = None) -> dict[str, Any]:
|
|
39
|
+
return self.request("POST", path, payload or {})
|
pbb/daemon/server.py
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"""Daemon process entry point."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
|
|
7
|
+
import uvicorn
|
|
8
|
+
|
|
9
|
+
from pbb.config import load_settings
|
|
10
|
+
from pbb.daemon.app import create_app
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def main() -> None:
|
|
14
|
+
parser = argparse.ArgumentParser()
|
|
15
|
+
parser.add_argument("--profile", required=True)
|
|
16
|
+
parser.add_argument("--browser")
|
|
17
|
+
parser.add_argument("--port", type=int)
|
|
18
|
+
args = parser.parse_args()
|
|
19
|
+
settings = load_settings()
|
|
20
|
+
if args.port:
|
|
21
|
+
settings.daemon_port = args.port
|
|
22
|
+
app = create_app(settings, args.profile, args.browser)
|
|
23
|
+
config = uvicorn.Config(
|
|
24
|
+
app,
|
|
25
|
+
host=settings.daemon_host,
|
|
26
|
+
port=settings.daemon_port,
|
|
27
|
+
log_level="info",
|
|
28
|
+
access_log=False,
|
|
29
|
+
)
|
|
30
|
+
server = uvicorn.Server(config)
|
|
31
|
+
app.state.server = server
|
|
32
|
+
server.run()
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
if __name__ == "__main__":
|
|
36
|
+
main()
|
pbb/utils/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Internal utility helpers."""
|
pbb/utils/paths.py
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"""Platform-specific paths owned by PBB."""
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
from platformdirs import PlatformDirs
|
|
6
|
+
|
|
7
|
+
from pbb.constants import APP_NAME
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def dirs() -> PlatformDirs:
|
|
11
|
+
return PlatformDirs(APP_NAME, appauthor=False, roaming=False)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def data_dir() -> Path:
|
|
15
|
+
return Path(dirs().user_data_path)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def config_dir() -> Path:
|
|
19
|
+
return Path(dirs().user_config_path)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def profiles_dir() -> Path:
|
|
23
|
+
return data_dir() / "profiles"
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def logs_dir() -> Path:
|
|
27
|
+
return data_dir() / "logs"
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def runtime_dir() -> Path:
|
|
31
|
+
return data_dir() / "runtime"
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def default_download_dir() -> Path:
|
|
35
|
+
return Path.home() / "Downloads" / "PBB"
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def ensure_app_dirs() -> None:
|
|
39
|
+
for path in (data_dir(), config_dir(), profiles_dir(), logs_dir(), runtime_dir()):
|
|
40
|
+
path.mkdir(parents=True, exist_ok=True)
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: persistent-browser-bridge
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Give AI coding agents a real persistent browser.
|
|
5
|
+
Author: Persistent Browser Bridge contributors
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
License-File: LICENSE
|
|
8
|
+
Keywords: ai-agents,browser-automation,persistent-profile,playwright
|
|
9
|
+
Classifier: Development Status :: 3 - Alpha
|
|
10
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
15
|
+
Classifier: Topic :: Software Development :: Testing
|
|
16
|
+
Requires-Python: >=3.11
|
|
17
|
+
Requires-Dist: fastapi<1,>=0.115
|
|
18
|
+
Requires-Dist: httpx<1,>=0.27
|
|
19
|
+
Requires-Dist: platformdirs<5,>=4.2
|
|
20
|
+
Requires-Dist: playwright<2,>=1.48
|
|
21
|
+
Requires-Dist: psutil<8,>=6
|
|
22
|
+
Requires-Dist: pydantic<3,>=2.9
|
|
23
|
+
Requires-Dist: rich<15,>=13.9
|
|
24
|
+
Requires-Dist: typer<1,>=0.12
|
|
25
|
+
Requires-Dist: uvicorn<1,>=0.30
|
|
26
|
+
Provides-Extra: dev
|
|
27
|
+
Requires-Dist: build<2,>=1.2; extra == 'dev'
|
|
28
|
+
Requires-Dist: mypy<2,>=1.13; extra == 'dev'
|
|
29
|
+
Requires-Dist: pytest-asyncio<1,>=0.24; extra == 'dev'
|
|
30
|
+
Requires-Dist: pytest<9,>=8.3; extra == 'dev'
|
|
31
|
+
Requires-Dist: ruff<1,>=0.8; extra == 'dev'
|
|
32
|
+
Requires-Dist: types-psutil<8,>=6; extra == 'dev'
|
|
33
|
+
Description-Content-Type: text/markdown
|
|
34
|
+
|
|
35
|
+
# Persistent Browser Bridge
|
|
36
|
+
|
|
37
|
+
> Give AI coding agents a real persistent browser.
|
|
38
|
+
|
|
39
|
+
Persistent Browser Bridge (PBB) is a local browser-control layer built on Playwright. Instead of forcing AI agents to repeatedly inspect screenshots, PBB exposes lightweight DOM-first browser actions while preserving real browser sessions across runs.
|
|
40
|
+
|
|
41
|
+
**Persistent. DOM-first. Local-first. Agent-friendly. Low-context.**
|
|
42
|
+
|
|
43
|
+
PBB is an early v0.1 release for normal, authorized browser automation. It is not a CAPTCHA bypass, anti-detection toolkit, credential collector, or scraping-evasion framework.
|
|
44
|
+
|
|
45
|
+
## Why PBB
|
|
46
|
+
|
|
47
|
+
Visual automation remains valuable when a page cannot be understood through the DOM. For routine forms, links, text, and downloads, a compact DOM snapshot is faster to inspect and less dependent on screen coordinates. A dedicated persistent browser profile also keeps cookies, local storage, IndexedDB, and login state between runs without touching your everyday browser profile.
|
|
48
|
+
|
|
49
|
+
| Feature | Computer Use | PBB |
|
|
50
|
+
|---|---|---|
|
|
51
|
+
| Interaction | Visual | DOM-first |
|
|
52
|
+
| Browser state | Depends on environment | Dedicated persistent profile |
|
|
53
|
+
| Login reuse | Environment dependent | Reuses its PBB profile |
|
|
54
|
+
| Element targeting | Vision and coordinates | DOM locators and snapshot references |
|
|
55
|
+
| Downloads | UI dependent | Playwright download events |
|
|
56
|
+
| Agent context | Visual-heavy | Compact DOM snapshot |
|
|
57
|
+
| Recovery | Agent dependent | Basic page and stale-lock recovery |
|
|
58
|
+
| Local-first | Depends | Yes |
|
|
59
|
+
|
|
60
|
+
PBB complements visual browser automation rather than replacing every use case.
|
|
61
|
+
|
|
62
|
+
## Architecture
|
|
63
|
+
|
|
64
|
+
```text
|
|
65
|
+
AI agent -> pbb CLI -> localhost daemon -> Playwright
|
|
66
|
+
-> real Edge/Chrome -> dedicated persistent profile -> website
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
The daemon binds to `127.0.0.1` by default and owns the Playwright process, active page, snapshot references, and download handling. CLI commands talk only to that local daemon, so a browser is not restarted for each action.
|
|
70
|
+
|
|
71
|
+
## Installation
|
|
72
|
+
|
|
73
|
+
PBB requires Python 3.11 or newer. Edge is preferred, Chrome is supported, and Playwright Chromium is the fallback.
|
|
74
|
+
|
|
75
|
+
```console
|
|
76
|
+
cd persistent-browser-bridge
|
|
77
|
+
python -m pip install -e .
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
If no system browser is available, install Playwright Chromium with `python -m playwright install chromium`. The package has not been published to PyPI yet; do not use `pip install persistent-browser-bridge` until a release announcement says otherwise.
|
|
81
|
+
|
|
82
|
+
## Quick start
|
|
83
|
+
|
|
84
|
+
```console
|
|
85
|
+
pbb doctor
|
|
86
|
+
pbb profile create default
|
|
87
|
+
pbb start --profile default
|
|
88
|
+
pbb open https://example.com
|
|
89
|
+
pbb snapshot
|
|
90
|
+
pbb text h1
|
|
91
|
+
pbb close
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
The first start opens a visible browser. Sign in manually when needed. PBB never asks for or stores account passwords; CAPTCHA, 2FA, device confirmation, and reauthentication remain user actions.
|
|
95
|
+
|
|
96
|
+
## Profiles
|
|
97
|
+
|
|
98
|
+
PBB profiles are separate from daily Edge and Chrome profiles:
|
|
99
|
+
|
|
100
|
+
```console
|
|
101
|
+
pbb profile create work
|
|
102
|
+
pbb profile list
|
|
103
|
+
pbb profile path work
|
|
104
|
+
pbb profile delete work
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Deletion requires confirmation (or explicit `--yes`) and only removes directories carrying PBB's own profile marker. PBB never deletes native Edge or Chrome lock files.
|
|
108
|
+
|
|
109
|
+
## CLI
|
|
110
|
+
|
|
111
|
+
| Command | Purpose |
|
|
112
|
+
|---|---|
|
|
113
|
+
| `pbb start --profile NAME` | Start the daemon and persistent browser |
|
|
114
|
+
| `pbb status` | Show process, profile, browser, page, and tabs |
|
|
115
|
+
| `pbb open URL` | Navigate and wait for `domcontentloaded` |
|
|
116
|
+
| `pbb snapshot` | Return a compact DOM snapshot |
|
|
117
|
+
| `pbb click TARGET` | Click a reference, selector, or semantic target |
|
|
118
|
+
| `pbb fill TARGET VALUE` | Fill a field; password values are never returned |
|
|
119
|
+
| `pbb text TARGET` | Read visible element text |
|
|
120
|
+
| `pbb download TARGET` | Wait for a real download event and save the file |
|
|
121
|
+
| `pbb screenshot [PATH]` | Capture a supporting screenshot |
|
|
122
|
+
| `pbb recover` | Clear stale PBB-owned locks |
|
|
123
|
+
| `pbb close` | Close browser and daemon |
|
|
124
|
+
|
|
125
|
+
Every action supports `--json`. Failures use a stable shape:
|
|
126
|
+
|
|
127
|
+
```json
|
|
128
|
+
{"success": false, "error": "element_not_found", "message": "...", "details": {}}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
## Snapshots and targets
|
|
132
|
+
|
|
133
|
+
`pbb snapshot --json` returns the page title, URL, headings, bounded visible text, and interactive elements. It excludes scripts, styles, hidden elements, cookies, password values, and hidden tokens. Internal selectors remain inside the daemon.
|
|
134
|
+
|
|
135
|
+
```json
|
|
136
|
+
{
|
|
137
|
+
"success": true,
|
|
138
|
+
"snapshot_id": "s_123abc",
|
|
139
|
+
"elements": [
|
|
140
|
+
{"id": 1, "role": "textbox", "name": "Email", "placeholder": "you@example.com"},
|
|
141
|
+
{"id": 2, "role": "button", "name": "Sign in"}
|
|
142
|
+
]
|
|
143
|
+
}
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
Use `@1`, `@2`, or the explicit `s_123abc:@2` form. PBB rejects stale references after navigation or when element identity changes. It resolves targets conservatively: snapshot reference, unique selector, exact button/link role, label, placeholder, then exact text. Multiple matches return `ambiguous_target`; PBB never chooses one at random.
|
|
147
|
+
|
|
148
|
+
PowerShell reserves `@` syntax, so quote references there: `pbb click '@2'`. Bash and similar shells accept `pbb click @2`.
|
|
149
|
+
|
|
150
|
+
## Downloads
|
|
151
|
+
|
|
152
|
+
```console
|
|
153
|
+
pbb download "Download PDF"
|
|
154
|
+
pbb download @5 --output ./downloads
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
PBB waits for Playwright's download event and uses `save_as`. A click that does not emit a download returns `download_not_triggered` rather than reporting false success. The default destination is `~/Downloads/PBB`.
|
|
158
|
+
|
|
159
|
+
## Configuration
|
|
160
|
+
|
|
161
|
+
PBB reads `config.toml` from the platform's application config directory. Supported keys are `browser`, `default_profile`, `daemon_host`, `daemon_port`, `download_dir`, `timeout`, `headless`, and `snapshot_max_chars`. v0.1 rejects non-loopback daemon hosts.
|
|
162
|
+
|
|
163
|
+
Environment overrides: `PBB_BROWSER`, `PBB_PROFILE`, `PBB_PORT`, `PBB_DOWNLOAD_DIR`, and `PBB_TIMEOUT`.
|
|
164
|
+
|
|
165
|
+
## Privacy and security
|
|
166
|
+
|
|
167
|
+
PBB runs locally and includes no telemetry. Browser profiles remain on the device. PBB does not upload browsing history, cookies, credentials, or profiles; does not export cookies; and does not access a password manager. Treat the profile directory as sensitive local data. See [SECURITY.md](SECURITY.md).
|
|
168
|
+
|
|
169
|
+
## Agent integration
|
|
170
|
+
|
|
171
|
+
Recommended flow: check status, start if needed, inspect `snapshot --json`, use DOM actions, and only fall back to screenshots or Computer Use when the DOM is insufficient. Full guidance is in [docs/agent-integration.md](docs/agent-integration.md).
|
|
172
|
+
|
|
173
|
+
### Codex integration
|
|
174
|
+
|
|
175
|
+
Copy this prompt into a project instruction:
|
|
176
|
+
|
|
177
|
+
```text
|
|
178
|
+
Always prefer Persistent Browser Bridge for browser automation when it is available.
|
|
179
|
+
|
|
180
|
+
Browser strategy:
|
|
181
|
+
1. Run `pbb status` and start PBB if needed.
|
|
182
|
+
2. Use `pbb snapshot --json` to inspect the current page.
|
|
183
|
+
3. Prefer `pbb click`, `pbb fill`, `pbb text`, and `pbb download`.
|
|
184
|
+
4. Reuse the existing persistent browser profile. Do not launch another browser unless necessary.
|
|
185
|
+
5. Use screenshots or Computer Use only when DOM-based interaction fails.
|
|
186
|
+
6. Never request or store account passwords.
|
|
187
|
+
7. Let the user manually complete authentication, CAPTCHA, 2FA, or device verification.
|
|
188
|
+
8. Continue using the same profile after authentication.
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
## Python SDK and MCP
|
|
192
|
+
|
|
193
|
+
A stable Python SDK and MCP server are planned for v0.2. The daemon's local HTTP API is an implementation detail in v0.1 and may change. No SDK or MCP support is claimed in this release.
|
|
194
|
+
|
|
195
|
+
## Limitations
|
|
196
|
+
|
|
197
|
+
- Snapshot references are daemon-memory state and do not survive daemon restarts.
|
|
198
|
+
- Shadow DOM, canvas-only controls, cross-origin frames, and highly virtualized UIs may need direct selectors or visual automation.
|
|
199
|
+
- Recovery covers closed pages and stale PBB locks; full crash replay is planned.
|
|
200
|
+
- One daemon controls one profile at a time in v0.1.
|
|
201
|
+
- Benchmarks are not published. PBB is designed to reduce repeated visual context usage, but no token or cost savings are claimed.
|
|
202
|
+
|
|
203
|
+
## Benchmark framework
|
|
204
|
+
|
|
205
|
+
The [benchmarks](benchmarks/README.md) directory defines a future comparison schema. Results are coming soon; no fabricated figures are included.
|
|
206
|
+
|
|
207
|
+
## Roadmap, contributing, and license
|
|
208
|
+
|
|
209
|
+
See [ROADMAP.md](ROADMAP.md), [CONTRIBUTING.md](CONTRIBUTING.md), and the [MIT License](LICENSE).
|
|
210
|
+
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
pbb/__init__.py,sha256=Bflqoe-oi00CP2bz5p7ySauUyAvEc_GrrjYT1fYeDB8,114
|
|
2
|
+
pbb/cli.py,sha256=DuUeAZaZADRbdTMZ7skZVhi2w2Cw0JfD7Wc2xzuKqak,10574
|
|
3
|
+
pbb/config.py,sha256=3HTHCU8S41ZCOrZ10HiTI4-5QE1RlVTUYMLHtn-vV84,2056
|
|
4
|
+
pbb/constants.py,sha256=GwuZNE00VnrIlRP4U6k_zgHkJusq6Y71ztzoRQ_yNPA,176
|
|
5
|
+
pbb/browser/__init__.py,sha256=-W8FIMfXuVVOa2pgQYCo4JcXbbjBVRqOsnXgbK13fWM,37
|
|
6
|
+
pbb/browser/browser_detection.py,sha256=BD_gEKJ0M0WS3FwoKNoEznSnTLqde0H1kdHXxDv4RG4,3830
|
|
7
|
+
pbb/browser/locator.py,sha256=PqEu-jI8bF3v2xP1qMMQ5LrDEoBtscZNZmQAyRrgW3k,4030
|
|
8
|
+
pbb/browser/locks.py,sha256=77N6uOKRQfRruzCl67vxk-IRFC_ptYDw4BtOW5GrBok,2312
|
|
9
|
+
pbb/browser/manager.py,sha256=aZGcJtgnkVaQ4xvSZjSWPTxg7FXXKVD5sCIdvalhDhQ,8516
|
|
10
|
+
pbb/browser/profile.py,sha256=uao-WiL0ebKB35S6Zlzjw-mq8M9CtmycTgWgq7LUlfA,1458
|
|
11
|
+
pbb/browser/snapshot.py,sha256=xSSanx2dZxWdyMtQDCorwl7HWxN55Bu8K1rYzZV4plk,6189
|
|
12
|
+
pbb/daemon/__init__.py,sha256=NWyGBC2FHXJ2lVldGx-_sp-KxGyNBezNOxfVsZu49CA,33
|
|
13
|
+
pbb/daemon/app.py,sha256=R5YlzQjTGPH7uZfjy9NjMx5whsmdMEZGsNES-Z7fUhw,4293
|
|
14
|
+
pbb/daemon/client.py,sha256=GSZ3LDYEj3Oi1J0iwYMuY9MPt0bXBXVDsdBlwWyedqA,1291
|
|
15
|
+
pbb/daemon/server.py,sha256=dsP_PGBCe5v5OM1BIgEhbVRXZYlvS7WHr0A5YI05ySY,846
|
|
16
|
+
pbb/utils/__init__.py,sha256=xtcYPLWPmpkNwPMZryCDOULwlGW3L01_LrnjEjGWh5g,32
|
|
17
|
+
pbb/utils/paths.py,sha256=qE1cR7Mr0oRhv4OxeQp2JkvxjHpOX9clifnkjcuwIQo,811
|
|
18
|
+
persistent_browser_bridge-0.1.0.dist-info/METADATA,sha256=Klq32BoE6IZETii69TYa1UzVw4FkU9J3DYBgPg8BG_o,9302
|
|
19
|
+
persistent_browser_bridge-0.1.0.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87
|
|
20
|
+
persistent_browser_bridge-0.1.0.dist-info/entry_points.txt,sha256=rC_3-Z9kUvFh-vpm9lhXtwrxT4YXOHo-jwU0DgO6xiQ,36
|
|
21
|
+
persistent_browser_bridge-0.1.0.dist-info/licenses/LICENSE,sha256=qwT2ZpRssWo6Hz_4g2cCs956dJM4YzgHbYjQuPaXLc0,1096
|
|
22
|
+
persistent_browser_bridge-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Persistent Browser Bridge contributors
|
|
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.
|
|
22
|
+
|