lucarne 1.0.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.
@@ -0,0 +1 @@
1
+ dist/
lucarne-1.0.0/PKG-INFO ADDED
@@ -0,0 +1,49 @@
1
+ Metadata-Version: 2.4
2
+ Name: lucarne
3
+ Version: 1.0.0
4
+ Summary: Python client for lucarne — self-hostable browser sessions you can drive, watch, and record.
5
+ Project-URL: Homepage, https://github.com/volter-ai/lucarne
6
+ Project-URL: Repository, https://github.com/volter-ai/lucarne
7
+ Project-URL: Changelog, https://github.com/volter-ai/lucarne/blob/main/CHANGELOG.md
8
+ Author: Aaron Volter
9
+ License-Expression: MIT
10
+ Keywords: agent,automation,browser,cdp,lucarne,playwright,remote-browser
11
+ Classifier: License :: OSI Approved :: MIT License
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Topic :: Internet :: WWW/HTTP :: Browsers
14
+ Classifier: Topic :: Software Development :: Testing
15
+ Requires-Python: >=3.8
16
+ Description-Content-Type: text/markdown
17
+
18
+ # lucarne (Python client)
19
+
20
+ Python client for **[lucarne](https://github.com/volter-ai/lucarne)** — self-hostable
21
+ browser sessions you can drive (CDP/Playwright), watch + control (porthole), and record,
22
+ on your own machine and your own IP.
23
+
24
+ This package is the **client**. The engine is a Node daemon — install and run it
25
+ separately (`npm install -g lucarne && lucarne serve`); you talk to it over HTTP, and
26
+ drive the browser over CDP with Playwright.
27
+
28
+ ```sh
29
+ pip install lucarne playwright
30
+ ```
31
+
32
+ ```python
33
+ from lucarne import LucarneClient
34
+ from playwright.sync_api import sync_playwright
35
+
36
+ luc = LucarneClient("http://127.0.0.1:7800") # token="..." if the daemon needs one
37
+ s = luc.create(profile="demo", backend="native")
38
+ print("watch:", s["viewUrl"])
39
+
40
+ with sync_playwright() as p:
41
+ page = p.chromium.connect_over_cdp(s["cdpUrl"]).contexts[0].pages[0]
42
+ page.goto("https://example.com", wait_until="domcontentloaded")
43
+ print(page.title())
44
+ ```
45
+
46
+ The client is stdlib-only (no dependencies) and covers
47
+ `health / create / list / get / destroy / act / content`. For the rest of the API, call
48
+ the HTTP endpoints directly — see the [OpenAPI spec](https://github.com/volter-ai/lucarne)
49
+ at `/openapi.json`. MIT © Aaron Volter.
@@ -0,0 +1,32 @@
1
+ # lucarne (Python client)
2
+
3
+ Python client for **[lucarne](https://github.com/volter-ai/lucarne)** — self-hostable
4
+ browser sessions you can drive (CDP/Playwright), watch + control (porthole), and record,
5
+ on your own machine and your own IP.
6
+
7
+ This package is the **client**. The engine is a Node daemon — install and run it
8
+ separately (`npm install -g lucarne && lucarne serve`); you talk to it over HTTP, and
9
+ drive the browser over CDP with Playwright.
10
+
11
+ ```sh
12
+ pip install lucarne playwright
13
+ ```
14
+
15
+ ```python
16
+ from lucarne import LucarneClient
17
+ from playwright.sync_api import sync_playwright
18
+
19
+ luc = LucarneClient("http://127.0.0.1:7800") # token="..." if the daemon needs one
20
+ s = luc.create(profile="demo", backend="native")
21
+ print("watch:", s["viewUrl"])
22
+
23
+ with sync_playwright() as p:
24
+ page = p.chromium.connect_over_cdp(s["cdpUrl"]).contexts[0].pages[0]
25
+ page.goto("https://example.com", wait_until="domcontentloaded")
26
+ print(page.title())
27
+ ```
28
+
29
+ The client is stdlib-only (no dependencies) and covers
30
+ `health / create / list / get / destroy / act / content`. For the rest of the API, call
31
+ the HTTP endpoints directly — see the [OpenAPI spec](https://github.com/volter-ai/lucarne)
32
+ at `/openapi.json`. MIT © Aaron Volter.
@@ -0,0 +1,52 @@
1
+ """Minimal Python client for the lucarne control API (stdlib only).
2
+
3
+ The daemon is the source of truth; this is convenience sugar. The session's
4
+ ``cdpUrl`` is still driven with your CDP client of choice (e.g. Playwright).
5
+ """
6
+ from __future__ import annotations
7
+
8
+ import json
9
+ import urllib.request
10
+ from typing import Any
11
+
12
+ __version__ = "1.0.0"
13
+
14
+
15
+ class LucarneClient:
16
+ def __init__(self, base_url: str = "http://127.0.0.1:7800", token: str | None = None) -> None:
17
+ self.base_url = base_url.rstrip("/")
18
+ self.token = token
19
+
20
+ def _req(self, method: str, path: str, body: Any | None = None) -> Any:
21
+ data = json.dumps(body).encode() if body is not None else None
22
+ req = urllib.request.Request(self.base_url + path, data=data, method=method)
23
+ if self.token:
24
+ req.add_header("Authorization", f"Bearer {self.token}")
25
+ if data is not None:
26
+ req.add_header("Content-Type", "application/json")
27
+ with urllib.request.urlopen(req) as resp:
28
+ raw = resp.read()
29
+ ctype = resp.headers.get("content-type", "")
30
+ return json.loads(raw) if "application/json" in ctype else raw.decode()
31
+
32
+ def health(self) -> dict:
33
+ return self._req("GET", "/health")
34
+
35
+ def create(self, **opts: Any) -> dict:
36
+ return self._req("POST", "/sessions", opts)
37
+
38
+ def list(self, **meta: str) -> list:
39
+ q = "?" + "&".join(f"meta.{k}={v}" for k, v in meta.items()) if meta else ""
40
+ return self._req("GET", "/sessions" + q)
41
+
42
+ def get(self, session_id: str) -> dict:
43
+ return self._req("GET", f"/sessions/{session_id}")
44
+
45
+ def destroy(self, session_id: str) -> dict:
46
+ return self._req("DELETE", f"/sessions/{session_id}")
47
+
48
+ def act(self, session_id: str, **action: Any) -> dict:
49
+ return self._req("POST", f"/sessions/{session_id}/act", action)
50
+
51
+ def content(self, session_id: str) -> str:
52
+ return self._req("GET", f"/sessions/{session_id}/content")
@@ -0,0 +1,31 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "lucarne"
7
+ version = "1.0.0"
8
+ description = "Python client for lucarne — self-hostable browser sessions you can drive, watch, and record."
9
+ readme = "README.md"
10
+ requires-python = ">=3.8"
11
+ license = "MIT"
12
+ authors = [{ name = "Aaron Volter" }]
13
+ keywords = ["browser", "playwright", "cdp", "automation", "remote-browser", "agent", "lucarne"]
14
+ classifiers = [
15
+ "License :: OSI Approved :: MIT License",
16
+ "Programming Language :: Python :: 3",
17
+ "Topic :: Software Development :: Testing",
18
+ "Topic :: Internet :: WWW/HTTP :: Browsers",
19
+ ]
20
+ dependencies = []
21
+
22
+ [project.urls]
23
+ Homepage = "https://github.com/volter-ai/lucarne"
24
+ Repository = "https://github.com/volter-ai/lucarne"
25
+ Changelog = "https://github.com/volter-ai/lucarne/blob/main/CHANGELOG.md"
26
+
27
+ [tool.hatch.build.targets.wheel]
28
+ only-include = ["lucarne.py"]
29
+
30
+ [tool.hatch.build.targets.sdist]
31
+ include = ["lucarne.py", "README.md", "pyproject.toml"]