pollyweb-cli 0.1.dev3__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,6 @@
1
+ .venv/
2
+ __pycache__/
3
+ *.pyc
4
+ .pytest_cache/
5
+ src/*.egg-info/
6
+ src/**/*.egg-info/
@@ -0,0 +1,10 @@
1
+ include README.md
2
+ include pyproject.toml
3
+ recursive-include docs *.md
4
+ recursive-include src *.py
5
+ recursive-include tests *.py
6
+ prune githooks
7
+ exclude .DS_Store
8
+ exclude AGENTS.md
9
+ exclude CLAUDE.md
10
+ exclude goal.yaml
@@ -0,0 +1,16 @@
1
+ Metadata-Version: 2.4
2
+ Name: pollyweb-cli
3
+ Version: 0.1.dev3
4
+ Summary: Command line wallet tooling built on the pollyweb library.
5
+ Project-URL: Homepage, https://pypi.org/project/pollyweb-cli/
6
+ Project-URL: Source, https://github.com/jorgemf/wallet-cli
7
+ Requires-Python: >=3.10
8
+ Description-Content-Type: text/markdown
9
+ Requires-Dist: pollyweb==1.0.4
10
+ Provides-Extra: dev
11
+ Requires-Dist: build>=1.2; extra == "dev"
12
+ Requires-Dist: pytest>=8.0; extra == "dev"
13
+
14
+ # pollyweb-cli
15
+
16
+ Command line wallet tooling built on top of the `pollyweb` Python package.
@@ -0,0 +1,3 @@
1
+ # pollyweb-cli
2
+
3
+ Command line wallet tooling built on top of the `pollyweb` Python package.
@@ -0,0 +1,25 @@
1
+ # Install
2
+
3
+ Install the CLI globally from PyPI with `pipx`:
4
+
5
+ ```bash
6
+ pipx install pollyweb-cli
7
+ ```
8
+
9
+ You can also install it globally with `pip`:
10
+
11
+ ```bash
12
+ python3 -m pip install pollyweb-cli
13
+ ```
14
+
15
+ If you want to install from a local checkout of this repository instead, run the commands from the repository root. In that case, `.` means "this folder":
16
+
17
+ ```bash
18
+ pipx install .
19
+ ```
20
+
21
+ or:
22
+
23
+ ```bash
24
+ python3 -m pip install .
25
+ ```
@@ -0,0 +1,18 @@
1
+ # Usage
2
+
3
+ Create your PollyWeb key pair:
4
+
5
+ ```bash
6
+ pw config
7
+ ```
8
+
9
+ This creates:
10
+
11
+ - `~/.pollyweb/private.pem`
12
+ - `~/.pollyweb/public.pem`
13
+
14
+ Overwrite an existing key pair:
15
+
16
+ ```bash
17
+ pw config --force
18
+ ```
@@ -0,0 +1,39 @@
1
+ [build-system]
2
+ requires = ["setuptools>=80", "wheel", "setuptools-scm[toml]>=8"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "pollyweb-cli"
7
+ dynamic = ["version"]
8
+ description = "Command line wallet tooling built on the pollyweb library."
9
+ readme = "README.md"
10
+ requires-python = ">=3.10"
11
+ dependencies = [
12
+ "pollyweb==1.0.4",
13
+ ]
14
+
15
+ [project.urls]
16
+ "Homepage" = "https://pypi.org/project/pollyweb-cli/"
17
+ "Source" = "https://github.com/jorgemf/wallet-cli"
18
+
19
+ [project.optional-dependencies]
20
+ dev = [
21
+ "build>=1.2",
22
+ "pytest>=8.0",
23
+ ]
24
+
25
+ [project.scripts]
26
+ pw = "pollyweb_cli.cli:main"
27
+
28
+ [tool.setuptools]
29
+ package-dir = {"" = "src"}
30
+
31
+ [tool.setuptools.packages.find]
32
+ where = ["src"]
33
+
34
+ [tool.pytest.ini_options]
35
+ testpaths = ["tests"]
36
+
37
+ [tool.setuptools_scm]
38
+ version_scheme = "guess-next-dev"
39
+ local_scheme = "no-local-version"
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1 @@
1
+ """pollyweb CLI package."""
@@ -0,0 +1,84 @@
1
+ from __future__ import annotations
2
+
3
+ import argparse
4
+ import sys
5
+ from pathlib import Path
6
+
7
+ from pollyweb import KeyPair
8
+ from cryptography.hazmat.primitives.serialization import (
9
+ Encoding,
10
+ NoEncryption,
11
+ PrivateFormat,
12
+ PublicFormat,
13
+ )
14
+
15
+
16
+ CONFIG_DIR = Path.home() / ".pollyweb"
17
+ PRIVATE_KEY_PATH = CONFIG_DIR / "private.pem"
18
+ PUBLIC_KEY_PATH = CONFIG_DIR / "public.pem"
19
+
20
+
21
+ def build_parser() -> argparse.ArgumentParser:
22
+ parser = argparse.ArgumentParser(
23
+ prog="pw",
24
+ description="PollyWeb command line wallet.",
25
+ )
26
+ subparsers = parser.add_subparsers(dest="command")
27
+
28
+ config_parser = subparsers.add_parser(
29
+ "config",
30
+ help="Generate a PollyWeb key pair in ~/.pollyweb.",
31
+ )
32
+ config_parser.add_argument(
33
+ "--force",
34
+ action="store_true",
35
+ help="Overwrite an existing key pair.",
36
+ )
37
+
38
+ return parser
39
+
40
+
41
+ def cmd_config(force: bool) -> int:
42
+ if not force and (PRIVATE_KEY_PATH.exists() or PUBLIC_KEY_PATH.exists()):
43
+ print(
44
+ "Key files already exist. Re-run with --force to overwrite them.",
45
+ file=sys.stderr,
46
+ )
47
+ return 1
48
+
49
+ CONFIG_DIR.mkdir(mode=0o700, parents=True, exist_ok=True)
50
+
51
+ key_pair = KeyPair()
52
+ private_pem = key_pair.PrivateKey.private_bytes(
53
+ encoding=Encoding.PEM,
54
+ format=PrivateFormat.PKCS8,
55
+ encryption_algorithm=NoEncryption(),
56
+ )
57
+ public_pem = key_pair.PublicKey.public_bytes(
58
+ encoding=Encoding.PEM,
59
+ format=PublicFormat.SubjectPublicKeyInfo,
60
+ )
61
+
62
+ PRIVATE_KEY_PATH.write_bytes(private_pem)
63
+ PUBLIC_KEY_PATH.write_bytes(public_pem)
64
+ PRIVATE_KEY_PATH.chmod(0o600)
65
+ PUBLIC_KEY_PATH.chmod(0o644)
66
+
67
+ print(f"Created {PRIVATE_KEY_PATH}")
68
+ print(f"Created {PUBLIC_KEY_PATH}")
69
+ return 0
70
+
71
+
72
+ def main(argv: list[str] | None = None) -> int:
73
+ parser = build_parser()
74
+ args = parser.parse_args(argv)
75
+
76
+ if args.command == "config":
77
+ return cmd_config(force=args.force)
78
+
79
+ parser.print_help()
80
+ return 0
81
+
82
+
83
+ if __name__ == "__main__":
84
+ raise SystemExit(main())
@@ -0,0 +1,16 @@
1
+ Metadata-Version: 2.4
2
+ Name: pollyweb-cli
3
+ Version: 0.1.dev3
4
+ Summary: Command line wallet tooling built on the pollyweb library.
5
+ Project-URL: Homepage, https://pypi.org/project/pollyweb-cli/
6
+ Project-URL: Source, https://github.com/jorgemf/wallet-cli
7
+ Requires-Python: >=3.10
8
+ Description-Content-Type: text/markdown
9
+ Requires-Dist: pollyweb==1.0.4
10
+ Provides-Extra: dev
11
+ Requires-Dist: build>=1.2; extra == "dev"
12
+ Requires-Dist: pytest>=8.0; extra == "dev"
13
+
14
+ # pollyweb-cli
15
+
16
+ Command line wallet tooling built on top of the `pollyweb` Python package.
@@ -0,0 +1,15 @@
1
+ .gitignore
2
+ MANIFEST.in
3
+ README.md
4
+ pyproject.toml
5
+ docs/install.md
6
+ docs/usage.md
7
+ src/pollyweb_cli/__init__.py
8
+ src/pollyweb_cli/cli.py
9
+ src/pollyweb_cli.egg-info/PKG-INFO
10
+ src/pollyweb_cli.egg-info/SOURCES.txt
11
+ src/pollyweb_cli.egg-info/dependency_links.txt
12
+ src/pollyweb_cli.egg-info/entry_points.txt
13
+ src/pollyweb_cli.egg-info/requires.txt
14
+ src/pollyweb_cli.egg-info/top_level.txt
15
+ tests/test_cli.py
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ pw = pollyweb_cli.cli:main
@@ -0,0 +1,5 @@
1
+ pollyweb==1.0.4
2
+
3
+ [dev]
4
+ build>=1.2
5
+ pytest>=8.0
@@ -0,0 +1 @@
1
+ pollyweb_cli
@@ -0,0 +1,85 @@
1
+ from __future__ import annotations
2
+
3
+ import stat
4
+ from pathlib import Path
5
+
6
+ from pollyweb_cli import cli
7
+
8
+
9
+ def test_config_creates_keypair_files(monkeypatch, tmp_path, capsys):
10
+ config_dir = tmp_path / ".pollyweb"
11
+ private_key_path = config_dir / "private.pem"
12
+ public_key_path = config_dir / "public.pem"
13
+
14
+ monkeypatch.setattr(cli, "CONFIG_DIR", config_dir)
15
+ monkeypatch.setattr(cli, "PRIVATE_KEY_PATH", private_key_path)
16
+ monkeypatch.setattr(cli, "PUBLIC_KEY_PATH", public_key_path)
17
+
18
+ exit_code = cli.main(["config"])
19
+
20
+ assert exit_code == 0
21
+ assert private_key_path.exists()
22
+ assert public_key_path.exists()
23
+ assert private_key_path.read_text().startswith("-----BEGIN PRIVATE KEY-----")
24
+ assert public_key_path.read_text().startswith("-----BEGIN PUBLIC KEY-----")
25
+
26
+ captured = capsys.readouterr()
27
+ assert str(private_key_path) in captured.out
28
+ assert str(public_key_path) in captured.out
29
+
30
+
31
+ def test_config_refuses_to_overwrite_existing_keys(monkeypatch, tmp_path, capsys):
32
+ config_dir = tmp_path / ".pollyweb"
33
+ config_dir.mkdir()
34
+ private_key_path = config_dir / "private.pem"
35
+ public_key_path = config_dir / "public.pem"
36
+ private_key_path.write_text("existing-private")
37
+ public_key_path.write_text("existing-public")
38
+
39
+ monkeypatch.setattr(cli, "CONFIG_DIR", config_dir)
40
+ monkeypatch.setattr(cli, "PRIVATE_KEY_PATH", private_key_path)
41
+ monkeypatch.setattr(cli, "PUBLIC_KEY_PATH", public_key_path)
42
+
43
+ exit_code = cli.main(["config"])
44
+
45
+ assert exit_code == 1
46
+ assert private_key_path.read_text() == "existing-private"
47
+ assert public_key_path.read_text() == "existing-public"
48
+
49
+ captured = capsys.readouterr()
50
+ assert "already exist" in captured.err
51
+
52
+
53
+ def test_config_force_overwrites_existing_keys(monkeypatch, tmp_path):
54
+ config_dir = tmp_path / ".pollyweb"
55
+ config_dir.mkdir()
56
+ private_key_path = config_dir / "private.pem"
57
+ public_key_path = config_dir / "public.pem"
58
+ private_key_path.write_text("existing-private")
59
+ public_key_path.write_text("existing-public")
60
+
61
+ monkeypatch.setattr(cli, "CONFIG_DIR", config_dir)
62
+ monkeypatch.setattr(cli, "PRIVATE_KEY_PATH", private_key_path)
63
+ monkeypatch.setattr(cli, "PUBLIC_KEY_PATH", public_key_path)
64
+
65
+ exit_code = cli.main(["config", "--force"])
66
+
67
+ assert exit_code == 0
68
+ assert private_key_path.read_text() != "existing-private"
69
+ assert public_key_path.read_text() != "existing-public"
70
+
71
+
72
+ def test_config_sets_expected_permissions(monkeypatch, tmp_path):
73
+ config_dir = tmp_path / ".pollyweb"
74
+ private_key_path = config_dir / "private.pem"
75
+ public_key_path = config_dir / "public.pem"
76
+
77
+ monkeypatch.setattr(cli, "CONFIG_DIR", config_dir)
78
+ monkeypatch.setattr(cli, "PRIVATE_KEY_PATH", private_key_path)
79
+ monkeypatch.setattr(cli, "PUBLIC_KEY_PATH", public_key_path)
80
+
81
+ exit_code = cli.main(["config"])
82
+
83
+ assert exit_code == 0
84
+ assert stat.S_IMODE(private_key_path.stat().st_mode) == 0o600
85
+ assert stat.S_IMODE(public_key_path.stat().st_mode) == 0o644