hvbrowser 0.1.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,5 @@
1
+ #!/usr/bin/env bash
2
+ set -eu
3
+
4
+ repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
5
+ exec "$repo_root/scripts/hooks/finalize-markdown.sh"
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env bash
2
+ set -eu
3
+
4
+ repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
5
+ exec "$repo_root/scripts/hooks/finalize-python.sh"
@@ -0,0 +1,46 @@
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "Read(**/*)",
5
+ "Write(**/*)",
6
+ "Edit(**/*)",
7
+ "Bash(docker:*)",
8
+ "Bash(ls:*)",
9
+ "Bash(mkdir:*)",
10
+ "Bash(tree:*)",
11
+ "Bash(find:*)",
12
+ "Bash(uv:*)",
13
+ "Bash(grep:*)",
14
+ "Bash(xargs cat)",
15
+ "Bash(git diff:*)",
16
+ "Bash(git -C * diff:*)",
17
+ "Bash(cat > *)",
18
+ "Bash(git add *)",
19
+ "Bash(git commit -m ' *)",
20
+ "Bash(uv run *)",
21
+ "Bash(uv run python -c ' *)",
22
+ "Bash(python3 -)",
23
+ "Bash(python3 -c ' *)",
24
+ "Bash(echo \"exit: $?\")"
25
+ ],
26
+ "additionalDirectories": [
27
+ "/tmp"
28
+ ]
29
+ },
30
+ "hooks": {
31
+ "Stop": [
32
+ {
33
+ "hooks": [
34
+ {
35
+ "type": "command",
36
+ "command": "scripts/hooks/finalize-python.sh"
37
+ },
38
+ {
39
+ "type": "command",
40
+ "command": "scripts/hooks/finalize-markdown.sh"
41
+ }
42
+ ]
43
+ }
44
+ ]
45
+ }
46
+ }
@@ -0,0 +1,63 @@
1
+ name: Publish to PyPI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ paths: [pyproject.toml]
7
+
8
+ jobs:
9
+ publish:
10
+ runs-on: ubuntu-latest
11
+ environment: pypi
12
+ permissions:
13
+ id-token: write
14
+ steps:
15
+ - uses: actions/checkout@v4
16
+ with:
17
+ fetch-depth: 2
18
+
19
+ - uses: actions/setup-python@v5
20
+ with:
21
+ python-version: "3.14"
22
+
23
+ - name: Install version checker
24
+ run: python -m pip install packaging
25
+
26
+ - name: Check version bump
27
+ id: version
28
+ run: |
29
+ CURRENT=$(python -c "
30
+ import tomllib
31
+ with open('pyproject.toml', 'rb') as f:
32
+ print(tomllib.load(f)['project']['version'])
33
+ ")
34
+ if git cat-file -e HEAD~1:pyproject.toml 2>/dev/null; then
35
+ PREVIOUS=$(git show HEAD~1:pyproject.toml | python -c "
36
+ import tomllib, sys
37
+ print(tomllib.load(sys.stdin.buffer)['project']['version'])
38
+ ")
39
+ else
40
+ PREVIOUS=""
41
+ fi
42
+ echo "current=$CURRENT" >> "$GITHUB_OUTPUT"
43
+ echo "previous=$PREVIOUS" >> "$GITHUB_OUTPUT"
44
+ if [ -z "$PREVIOUS" ]; then
45
+ echo "bumped=true" >> "$GITHUB_OUTPUT"
46
+ elif [ "$CURRENT" != "$PREVIOUS" ]; then
47
+ python -c "
48
+ from packaging.version import Version
49
+ current, previous = Version('$CURRENT'), Version('$PREVIOUS')
50
+ exit(0 if current > previous else 1)
51
+ "
52
+ echo "bumped=true" >> "$GITHUB_OUTPUT"
53
+ else
54
+ echo "bumped=false" >> "$GITHUB_OUTPUT"
55
+ fi
56
+
57
+ - name: Build
58
+ if: steps.version.outputs.bumped == 'true'
59
+ run: pip install build && python -m build
60
+
61
+ - name: Publish
62
+ if: steps.version.outputs.bumped == 'true'
63
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,9 @@
1
+ {
2
+ "recommendations": [
3
+ "ms-python.python",
4
+ "charliermarsh.ruff",
5
+ "ms-python.black-formatter",
6
+ "ms-python.mypy-type-checker",
7
+ "DavidAnson.vscode-markdownlint"
8
+ ]
9
+ }
@@ -0,0 +1,28 @@
1
+ {
2
+ "python.analysis.typeCheckingMode": "off",
3
+ "python.analysis.diagnosticSeverityOverrides": {
4
+ "reportMissingModuleSource": "none",
5
+ "reportMissingImports": "none"
6
+ },
7
+ "editor.formatOnSave": true,
8
+ "[python]": {
9
+ "editor.defaultFormatter": "ms-python.black-formatter",
10
+ "editor.codeActionsOnSave": {
11
+ "source.fixAll.ruff": "always"
12
+ }
13
+ },
14
+ "black-formatter.importStrategy": "fromEnvironment",
15
+ "ruff.importStrategy": "fromEnvironment",
16
+ "[markdown]": {
17
+ "editor.defaultFormatter": "DavidAnson.vscode-markdownlint"
18
+ },
19
+ "files.trimTrailingWhitespace": true,
20
+ "files.insertFinalNewline": true,
21
+ "editor.rulers": [
22
+ 88
23
+ ],
24
+ "mypy-type-checker.importStrategy": "fromEnvironment",
25
+ "mypy-type-checker.args": [
26
+ "--config-file=mypy.ini"
27
+ ]
28
+ }
@@ -0,0 +1,12 @@
1
+ # Agent Instructions
2
+
3
+ The repository keeps provider-neutral finalization scripts in `scripts/hooks/`.
4
+ Do not create separate copies under an individual agent's configuration
5
+ directory.
6
+
7
+ - Before development, create a clean environment with
8
+ `bash scripts/rebuild-env.sh`.
9
+ - After changing Python, run `bash scripts/hooks/finalize-python.sh`.
10
+ - After changing Markdown, run `bash scripts/hooks/finalize-markdown.sh`.
11
+ - Keep the committed dependency source for `hbrowser` on PyPI. Install a local
12
+ editable checkout manually only after rebuilding the environment.
@@ -0,0 +1,86 @@
1
+ Metadata-Version: 2.4
2
+ Name: hvbrowser
3
+ Version: 0.1.0
4
+ Summary: Browser automation APIs for HentaiVerse.
5
+ Project-URL: Homepage, https://github.com/Kuan-Lun/hvbrowser
6
+ Project-URL: Tracker, https://github.com/Kuan-Lun/hvbrowser/issues
7
+ Author: Kuan-Lun Wang
8
+ License: GNU Affero General Public License v3
9
+ Classifier: Development Status :: 3 - Alpha
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: License :: OSI Approved :: GNU Affero General Public License v3 or later (AGPLv3+)
12
+ Classifier: Operating System :: OS Independent
13
+ Classifier: Programming Language :: Python :: 3.14
14
+ Requires-Python: <3.15,>=3.14
15
+ Requires-Dist: hbrowser<0.36,>=0.35.0
16
+ Provides-Extra: dev
17
+ Requires-Dist: black>=26.5.1; extra == 'dev'
18
+ Requires-Dist: mypy>=2.1.0; extra == 'dev'
19
+ Requires-Dist: pymarkdownlnt>=0.9.38; extra == 'dev'
20
+ Requires-Dist: pytest>=9.1.1; extra == 'dev'
21
+ Requires-Dist: ruff>=0.15.20; extra == 'dev'
22
+ Description-Content-Type: text/markdown
23
+
24
+ # HVBrowser
25
+
26
+ HVBrowser provides browser automation APIs for HentaiVerse. It builds on
27
+ `hbrowser` for the shared authenticated browser session.
28
+
29
+ The package was extracted from the historical combined `hbrowser`
30
+ distribution. Battle-domain APIs live in the separate `hvbattle` package.
31
+
32
+ ## Development
33
+
34
+ Build a clean environment backed by the PyPI release of `hbrowser`:
35
+
36
+ ```bash
37
+ bash scripts/rebuild-env.sh
38
+ ```
39
+
40
+ For coordinated local development, overlay the clean environment manually:
41
+
42
+ ```bash
43
+ uv pip install --python .venv/bin/python --reinstall --no-deps --editable \
44
+ /Users/kuanlun_wang/Desktop/git-repo/hbrowser.clone
45
+ ```
46
+
47
+ Commands that must preserve this editable overlay use `uv run --no-sync`.
48
+
49
+ ## Read-only Market inspection
50
+
51
+ Market inspection and sale planning are non-mutating by default:
52
+
53
+ ```python
54
+ import asyncio
55
+
56
+ from hvbrowser import HVDriver, SellItems
57
+
58
+
59
+ async def main() -> None:
60
+ async with HVDriver(headless=True) as driver:
61
+ snapshot = await driver.inspect_market()
62
+ plan = await driver.marketcheck(SellItems(materials=["Low-Grade Metals"]))
63
+ print(len(snapshot.items), plan.total_units)
64
+
65
+
66
+ asyncio.run(main())
67
+ ```
68
+
69
+ The compatibility `marketcheck()` call requires `commit=True` before it can
70
+ request submission. Submission is currently blocked by a fail-closed runtime
71
+ gate until the current quote selector and pricing semantics have been confirmed
72
+ with the read-only live probe. The guarded implementation never deposits
73
+ credits and never re-lists unrelated orders.
74
+
75
+ ## Guarded live smoke test
76
+
77
+ The surrounding `hentaiverse` workspace owns account selection. This smoke
78
+ probe only refuses to construct a browser unless credentials are supplied
79
+ indirectly through the environment. It stops when an active battle is detected
80
+ and contains no click, submit, repair, recovery, lottery, monster-lab, or battle
81
+ call:
82
+
83
+ ```bash
84
+ uv run --no-sync python scripts/live_readonly_smoke.py \
85
+ --inspect-market-form
86
+ ```
@@ -0,0 +1,63 @@
1
+ # HVBrowser
2
+
3
+ HVBrowser provides browser automation APIs for HentaiVerse. It builds on
4
+ `hbrowser` for the shared authenticated browser session.
5
+
6
+ The package was extracted from the historical combined `hbrowser`
7
+ distribution. Battle-domain APIs live in the separate `hvbattle` package.
8
+
9
+ ## Development
10
+
11
+ Build a clean environment backed by the PyPI release of `hbrowser`:
12
+
13
+ ```bash
14
+ bash scripts/rebuild-env.sh
15
+ ```
16
+
17
+ For coordinated local development, overlay the clean environment manually:
18
+
19
+ ```bash
20
+ uv pip install --python .venv/bin/python --reinstall --no-deps --editable \
21
+ /Users/kuanlun_wang/Desktop/git-repo/hbrowser.clone
22
+ ```
23
+
24
+ Commands that must preserve this editable overlay use `uv run --no-sync`.
25
+
26
+ ## Read-only Market inspection
27
+
28
+ Market inspection and sale planning are non-mutating by default:
29
+
30
+ ```python
31
+ import asyncio
32
+
33
+ from hvbrowser import HVDriver, SellItems
34
+
35
+
36
+ async def main() -> None:
37
+ async with HVDriver(headless=True) as driver:
38
+ snapshot = await driver.inspect_market()
39
+ plan = await driver.marketcheck(SellItems(materials=["Low-Grade Metals"]))
40
+ print(len(snapshot.items), plan.total_units)
41
+
42
+
43
+ asyncio.run(main())
44
+ ```
45
+
46
+ The compatibility `marketcheck()` call requires `commit=True` before it can
47
+ request submission. Submission is currently blocked by a fail-closed runtime
48
+ gate until the current quote selector and pricing semantics have been confirmed
49
+ with the read-only live probe. The guarded implementation never deposits
50
+ credits and never re-lists unrelated orders.
51
+
52
+ ## Guarded live smoke test
53
+
54
+ The surrounding `hentaiverse` workspace owns account selection. This smoke
55
+ probe only refuses to construct a browser unless credentials are supplied
56
+ indirectly through the environment. It stops when an active battle is detected
57
+ and contains no click, submit, repair, recovery, lottery, monster-lab, or battle
58
+ call:
59
+
60
+ ```bash
61
+ uv run --no-sync python scripts/live_readonly_smoke.py \
62
+ --inspect-market-form
63
+ ```
@@ -0,0 +1,16 @@
1
+ [mypy]
2
+ python_version = 3.14
3
+ warn_unused_configs = True
4
+ disallow_untyped_defs = True
5
+ check_untyped_defs = True
6
+ warn_redundant_casts = True
7
+ warn_unused_ignores = True
8
+ no_implicit_optional = True
9
+ strict_equality = True
10
+ warn_return_any = True
11
+ disallow_any_generics = True
12
+ disallow_subclassing_any = True
13
+ disallow_untyped_calls = True
14
+ disallow_incomplete_defs = True
15
+ disallow_untyped_decorators = True
16
+ no_implicit_reexport = True
@@ -0,0 +1,46 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "hvbrowser"
7
+ version = "0.1.0"
8
+ description = "Browser automation APIs for HentaiVerse."
9
+ readme = "README.md"
10
+ authors = [{ name = "Kuan-Lun Wang" }]
11
+ license = { text = "GNU Affero General Public License v3" }
12
+ requires-python = ">=3.14,<3.15"
13
+ dependencies = [
14
+ "hbrowser>=0.35.0,<0.36",
15
+ ]
16
+
17
+ classifiers = [
18
+ "Development Status :: 3 - Alpha",
19
+ "Intended Audience :: Developers",
20
+ "Operating System :: OS Independent",
21
+ "License :: OSI Approved :: GNU Affero General Public License v3 or later (AGPLv3+)",
22
+ "Programming Language :: Python :: 3.14",
23
+ ]
24
+
25
+ [project.optional-dependencies]
26
+ dev = [
27
+ "black>=26.5.1",
28
+ "ruff>=0.15.20",
29
+ "mypy>=2.1.0",
30
+ "pymarkdownlnt>=0.9.38",
31
+ "pytest>=9.1.1",
32
+ ]
33
+
34
+ [project.urls]
35
+ Homepage = "https://github.com/Kuan-Lun/hvbrowser"
36
+ Tracker = "https://github.com/Kuan-Lun/hvbrowser/issues"
37
+
38
+ [tool.hatch.build.targets.wheel]
39
+ packages = ["src/hvbrowser"]
40
+
41
+ [tool.ruff]
42
+ line-length = 88
43
+
44
+ [tool.ruff.lint]
45
+ select = ["E", "F", "I", "UP"]
46
+ extend-ignore = ["E203", "E226", "E501"]
@@ -0,0 +1,30 @@
1
+ #!/usr/bin/env bash
2
+ # Shared finalizer for Markdown changes.
3
+
4
+ set -uo pipefail
5
+
6
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
7
+ REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
8
+ cd "$REPO_ROOT"
9
+
10
+ MD_FILES=()
11
+ while IFS= read -r file; do
12
+ MD_FILES+=("$file")
13
+ done < <(
14
+ find . -maxdepth 2 -type f -name "*.md" \
15
+ -not -path "./.venv/*" \
16
+ -not -path "./node_modules/*" \
17
+ -not -path "./.pytest_cache/*" \
18
+ -not -path "./.*" \
19
+ | sort
20
+ )
21
+
22
+ if [ ${#MD_FILES[@]} -eq 0 ]; then
23
+ exit 0
24
+ fi
25
+
26
+ uv run --no-sync pymarkdown fix "${MD_FILES[@]}" >/dev/null 2>&1 || true
27
+
28
+ if ! uv run --no-sync ruff format --preview "${MD_FILES[@]}" >&2; then
29
+ exit 2
30
+ fi
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env bash
2
+ # Shared finalizer for Python changes. `--no-sync` preserves a manually
3
+ # installed editable hbrowser checkout in the project environment.
4
+
5
+ set -eu
6
+ trap 'exit 2' ERR
7
+
8
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
9
+ REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
10
+ cd "$REPO_ROOT"
11
+
12
+ FORMAT_PATHS=(src/hvbrowser tests scripts/live_readonly_smoke.py)
13
+ TYPE_PATHS=(src/hvbrowser scripts/live_readonly_smoke.py)
14
+
15
+ uv run --no-sync black "${FORMAT_PATHS[@]}" >&2
16
+ uv run --no-sync ruff check --fix "${FORMAT_PATHS[@]}" >&2
17
+ uv run --no-sync black "${FORMAT_PATHS[@]}" >&2
18
+ uv run --no-sync mypy "${TYPE_PATHS[@]}" >&2
@@ -0,0 +1,25 @@
1
+ #!/usr/bin/env python3
2
+ """Run the guarded, non-mutating HentaiVerse smoke probe."""
3
+
4
+ import argparse
5
+ import asyncio
6
+ import json
7
+ from dataclasses import asdict
8
+
9
+ from hvbrowser.live_probe import run_live_probe
10
+
11
+
12
+ def main() -> None:
13
+ parser = argparse.ArgumentParser(description=__doc__)
14
+ parser.add_argument(
15
+ "--inspect-market-form",
16
+ action="store_true",
17
+ help="Inspect one sell form and its public quote without clicking it",
18
+ )
19
+ args = parser.parse_args()
20
+ result = asyncio.run(run_live_probe(inspect_market_form=args.inspect_market_form))
21
+ print(json.dumps(asdict(result), indent=2))
22
+
23
+
24
+ if __name__ == "__main__":
25
+ main()
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env bash
2
+ # Rebuild a clean development environment whose hbrowser dependency comes
3
+ # from PyPI. Install a local editable hbrowser checkout manually afterwards
4
+ # when cross-repository development is required.
5
+
6
+ set -euo pipefail
7
+ cd "$(dirname "$0")/.."
8
+
9
+ rm -rf .venv .mypy_cache .ruff_cache .pytest_cache
10
+ find . -type d -name __pycache__ -exec rm -rf {} +
11
+ uv cache clean --force
12
+ uv venv --python 3.14
13
+ uv pip install -e ".[dev]"
@@ -0,0 +1,32 @@
1
+ __all__ = [
2
+ "HVDriver",
3
+ "SellItems",
4
+ "MarketCategory",
5
+ "MarketClient",
6
+ "MarketItem",
7
+ "MarketPageError",
8
+ "MarketSale",
9
+ "MarketSalePlan",
10
+ "MarketSaleQuote",
11
+ "MarketSaleReport",
12
+ "MarketSnapshot",
13
+ "MarketSubmissionError",
14
+ "HENTAIVERSE_ISEKAI_ROOT_URL",
15
+ "HENTAIVERSE_ROOT_URL",
16
+ ]
17
+
18
+
19
+ from .hv import HVDriver, SellItems
20
+ from .market import (
21
+ MarketCategory,
22
+ MarketClient,
23
+ MarketItem,
24
+ MarketPageError,
25
+ MarketSale,
26
+ MarketSalePlan,
27
+ MarketSaleQuote,
28
+ MarketSaleReport,
29
+ MarketSnapshot,
30
+ MarketSubmissionError,
31
+ )
32
+ from .urls import HENTAIVERSE_ISEKAI_ROOT_URL, HENTAIVERSE_ROOT_URL