fvpnctl 0.2.0__tar.gz → 0.2.2__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 (28) hide show
  1. {fvpnctl-0.2.0 → fvpnctl-0.2.2}/PKG-INFO +9 -3
  2. {fvpnctl-0.2.0 → fvpnctl-0.2.2}/README.md +7 -2
  3. fvpnctl-0.2.2/pyproject.toml +84 -0
  4. {fvpnctl-0.2.0 → fvpnctl-0.2.2}/src/fvpnctl/__init__.py +1 -1
  5. {fvpnctl-0.2.0 → fvpnctl-0.2.2}/src/fvpnctl/cli.py +10 -0
  6. {fvpnctl-0.2.0 → fvpnctl-0.2.2}/uv.lock +15 -19
  7. fvpnctl-0.2.0/pyproject.toml +0 -58
  8. {fvpnctl-0.2.0 → fvpnctl-0.2.2}/.github/workflows/ci.yml +0 -0
  9. {fvpnctl-0.2.0 → fvpnctl-0.2.2}/.gitignore +0 -0
  10. {fvpnctl-0.2.0 → fvpnctl-0.2.2}/.pre-commit-config.yaml +0 -0
  11. {fvpnctl-0.2.0 → fvpnctl-0.2.2}/LICENSE +0 -0
  12. {fvpnctl-0.2.0 → fvpnctl-0.2.2}/contrib/com.fvpnctl.headless.plist +0 -0
  13. {fvpnctl-0.2.0 → fvpnctl-0.2.2}/docs/how-it-works.md +0 -0
  14. {fvpnctl-0.2.0 → fvpnctl-0.2.2}/docs/superpowers/specs/2026-06-20-fortivpn-cli-via-cdp-design.md +0 -0
  15. {fvpnctl-0.2.0 → fvpnctl-0.2.2}/src/fvpnctl/cdp.py +0 -0
  16. {fvpnctl-0.2.0 → fvpnctl-0.2.2}/src/fvpnctl/controller.py +0 -0
  17. {fvpnctl-0.2.0 → fvpnctl-0.2.2}/src/fvpnctl/errors.py +0 -0
  18. {fvpnctl-0.2.0 → fvpnctl-0.2.2}/src/fvpnctl/keychain.py +0 -0
  19. {fvpnctl-0.2.0 → fvpnctl-0.2.2}/src/fvpnctl/launcher.py +0 -0
  20. {fvpnctl-0.2.0 → fvpnctl-0.2.2}/src/fvpnctl/monitor.py +0 -0
  21. {fvpnctl-0.2.0 → fvpnctl-0.2.2}/tests/manual/test_live.py +0 -0
  22. {fvpnctl-0.2.0 → fvpnctl-0.2.2}/tests/test_cdp.py +0 -0
  23. {fvpnctl-0.2.0 → fvpnctl-0.2.2}/tests/test_cli.py +0 -0
  24. {fvpnctl-0.2.0 → fvpnctl-0.2.2}/tests/test_controller.py +0 -0
  25. {fvpnctl-0.2.0 → fvpnctl-0.2.2}/tests/test_errors.py +0 -0
  26. {fvpnctl-0.2.0 → fvpnctl-0.2.2}/tests/test_keychain.py +0 -0
  27. {fvpnctl-0.2.0 → fvpnctl-0.2.2}/tests/test_launcher.py +0 -0
  28. {fvpnctl-0.2.0 → fvpnctl-0.2.2}/tests/test_monitor.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fvpnctl
3
- Version: 0.2.0
3
+ Version: 0.2.2
4
4
  Summary: Control your FortiClient VPN from the macOS command line.
5
5
  Project-URL: Homepage, https://github.com/mpasternak/fortivpn-cli
6
6
  Project-URL: Repository, https://github.com/mpasternak/fortivpn-cli
@@ -19,6 +19,7 @@ Classifier: Programming Language :: Python :: 3.12
19
19
  Classifier: Programming Language :: Python :: 3.13
20
20
  Classifier: Topic :: System :: Networking
21
21
  Requires-Python: >=3.11
22
+ Requires-Dist: setuptools<0; sys_platform == 'win32'
22
23
  Description-Content-Type: text/markdown
23
24
 
24
25
  # fvpnctl — control your FortiClient VPN from the macOS command line
@@ -31,7 +32,9 @@ handy for scripts, automation, and headless or over-SSH use. It works with a For
31
32
  already have running, talking to it over a local debugging port.
32
33
 
33
34
  - Zero runtime dependencies (Python standard library only).
34
- - Python ≥ 3.11, macOS only.
35
+ - **macOS only** (Python ≥ 3.11). There is **no Windows support** — it drives the
36
+ macOS FortiClient.app, so `pip` / `uv` / `uvx` deliberately **refuse to install on
37
+ Windows**, and running it from a source checkout there exits with a clear error.
35
38
  - Attach-only by default: commands attach to a running FortiClient; the one explicit
36
39
  exception is `fvpnctl startserver`, an opt-in launcher you invoke yourself.
37
40
 
@@ -113,7 +116,10 @@ uv tool install . # install the `fvpnctl` command from the local tree
113
116
  uv run fvpnctl list # or run it in place, without installing
114
117
  ```
115
118
 
116
- Requirements: macOS, Python ≥ 3.11.
119
+ Requirements: macOS, Python ≥ 3.11. **Windows is not supported** — installation on
120
+ Windows fails on purpose (the package declares an unsatisfiable `win32` requirement),
121
+ and there is no Linux support for the runtime either (the CLI talks to macOS
122
+ FortiClient.app). Linux is used only by CI to run the mocked test suite.
117
123
 
118
124
  ---
119
125
 
@@ -8,7 +8,9 @@ handy for scripts, automation, and headless or over-SSH use. It works with a For
8
8
  already have running, talking to it over a local debugging port.
9
9
 
10
10
  - Zero runtime dependencies (Python standard library only).
11
- - Python ≥ 3.11, macOS only.
11
+ - **macOS only** (Python ≥ 3.11). There is **no Windows support** — it drives the
12
+ macOS FortiClient.app, so `pip` / `uv` / `uvx` deliberately **refuse to install on
13
+ Windows**, and running it from a source checkout there exits with a clear error.
12
14
  - Attach-only by default: commands attach to a running FortiClient; the one explicit
13
15
  exception is `fvpnctl startserver`, an opt-in launcher you invoke yourself.
14
16
 
@@ -90,7 +92,10 @@ uv tool install . # install the `fvpnctl` command from the local tree
90
92
  uv run fvpnctl list # or run it in place, without installing
91
93
  ```
92
94
 
93
- Requirements: macOS, Python ≥ 3.11.
95
+ Requirements: macOS, Python ≥ 3.11. **Windows is not supported** — installation on
96
+ Windows fails on purpose (the package declares an unsatisfiable `win32` requirement),
97
+ and there is no Linux support for the runtime either (the CLI talks to macOS
98
+ FortiClient.app). Linux is used only by CI to run the mocked test suite.
94
99
 
95
100
  ---
96
101
 
@@ -0,0 +1,84 @@
1
+ [project]
2
+ name = "fvpnctl"
3
+ version = "0.2.2"
4
+ requires-python = ">=3.11"
5
+ readme = "README.md"
6
+ description = "Control your FortiClient VPN from the macOS command line."
7
+ license = "MIT"
8
+ license-files = ["LICENSE"]
9
+ authors = [{ name = "Michał Pasternak" }]
10
+ keywords = ["forticlient", "fortinet", "vpn", "ipsec", "macos", "cli"]
11
+ classifiers = [
12
+ "Development Status :: 4 - Beta",
13
+ "Environment :: Console",
14
+ "Intended Audience :: System Administrators",
15
+ "Operating System :: MacOS",
16
+ "Programming Language :: Python :: 3",
17
+ "Programming Language :: Python :: 3.11",
18
+ "Programming Language :: Python :: 3.12",
19
+ "Programming Language :: Python :: 3.13",
20
+ "Topic :: System :: Networking",
21
+ ]
22
+ # Zero runtime dependencies is a hard design requirement on supported platforms:
23
+ # the tool uses only the Python standard library (socket, json, urllib,
24
+ # subprocess, argparse). See the design spec sections 2 and 8 for the rationale
25
+ # (no third-party attack surface, trivially auditable, runs against any stock
26
+ # Python 3.11+).
27
+ #
28
+ # The single entry below is NOT a real dependency. fvpnctl is macOS-only (it
29
+ # drives the FortiClient.app debug port) and has no Windows support, so this is an
30
+ # intentionally unsatisfiable requirement gated to win32: on Windows pip/uv/uvx
31
+ # fail fast instead of installing something that cannot work. On macOS — and on
32
+ # Linux, where CI runs the platform-agnostic mocked test suite — the marker is
33
+ # false, so the dependency set stays empty.
34
+ #
35
+ # IMPORTANT: the constraint pins a PyPA-owned package (setuptools) to an
36
+ # impossible version, NOT a made-up package name. A non-existent placeholder name
37
+ # would be a dependency-confusion hole: an attacker could register that name on
38
+ # PyPI, and then `pip install fvpnctl` on Windows would *succeed* and pull in
39
+ # their code instead of failing. No PEP 440 version is < 0, so `setuptools<0` can
40
+ # never be satisfied by anyone — setuptools is PyPA-controlled and cannot be
41
+ # squatted — making this a permanently safe, unsatisfiable gate. cli.main() also
42
+ # refuses to run on win32 as a runtime backstop.
43
+ dependencies = [
44
+ "setuptools<0; sys_platform == 'win32'",
45
+ ]
46
+
47
+ [project.urls]
48
+ Homepage = "https://github.com/mpasternak/fortivpn-cli"
49
+ Repository = "https://github.com/mpasternak/fortivpn-cli"
50
+ Issues = "https://github.com/mpasternak/fortivpn-cli/issues"
51
+
52
+ [project.scripts]
53
+ fvpnctl = "fvpnctl.cli:main"
54
+
55
+ [dependency-groups]
56
+ # Installed by default on `uv sync` so the test suite + lint/format run out of the
57
+ # box and reproducibly in CI (ruff is a real dev dep, not relied on from PATH).
58
+ dev = ["pytest>=8", "ruff>=0.13"]
59
+
60
+ [build-system]
61
+ requires = ["hatchling"]
62
+ build-backend = "hatchling.build"
63
+
64
+ [tool.hatch.build.targets.wheel]
65
+ packages = ["src/fvpnctl"]
66
+
67
+ [tool.uv]
68
+ # fvpnctl is macOS-only; CI additionally resolves on Linux to run the mocked,
69
+ # platform-agnostic test suite. Restrict universal locking to these so `uv lock`
70
+ # does not try (and fail) to resolve the intentionally-unsatisfiable win32 install
71
+ # guard declared in [project].dependencies.
72
+ environments = ["sys_platform == 'darwin'", "sys_platform == 'linux'"]
73
+
74
+ [tool.ruff]
75
+ line-length = 100
76
+ target-version = "py311"
77
+
78
+ [tool.ruff.lint]
79
+ # E/W pycodestyle, F pyflakes, I import sorting, UP pyupgrade, B bugbear.
80
+ select = ["E", "F", "I", "UP", "B", "W"]
81
+
82
+ [tool.pytest.ini_options]
83
+ testpaths = ["tests"]
84
+ addopts = "-q"
@@ -1,3 +1,3 @@
1
1
  """fvpnctl — control FortiClient IPsec VPN on macOS via the Chrome DevTools Protocol."""
2
2
 
3
- __version__ = "0.2.0"
3
+ __version__ = "0.2.2"
@@ -479,6 +479,16 @@ def main(argv: list[str] | None = None) -> int:
479
479
  """
480
480
  global _VERBOSE
481
481
 
482
+ if sys.platform == "win32":
483
+ # fvpnctl drives the macOS FortiClient.app debug port; there is no Windows
484
+ # support. The packaging metadata already refuses to *install* on win32
485
+ # (see pyproject.toml), but a source checkout run via `python -m` / `uv run`
486
+ # can still reach here — fail fast with a clear message instead of a
487
+ # confusing "FortiClient not found" later. (win32-only so the Linux CI that
488
+ # runs the mocked test suite via main() is unaffected.)
489
+ print("fvpnctl is macOS-only and has no Windows support.", file=sys.stderr)
490
+ return 1
491
+
482
492
  parser = _build_parser()
483
493
  args = parser.parse_args(argv)
484
494
 
@@ -1,28 +1,28 @@
1
1
  version = 1
2
2
  revision = 3
3
3
  requires-python = ">=3.11"
4
-
5
- [[package]]
6
- name = "colorama"
7
- version = "0.4.6"
8
- source = { registry = "https://pypi.org/simple" }
9
- sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" }
10
- wheels = [
11
- { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" },
4
+ resolution-markers = [
5
+ "sys_platform == 'darwin'",
6
+ "sys_platform == 'linux'",
7
+ ]
8
+ supported-markers = [
9
+ "sys_platform == 'darwin'",
10
+ "sys_platform == 'linux'",
12
11
  ]
13
12
 
14
13
  [[package]]
15
14
  name = "fvpnctl"
16
- version = "0.2.0"
15
+ version = "0.2.2"
17
16
  source = { editable = "." }
18
17
 
19
18
  [package.dev-dependencies]
20
19
  dev = [
21
- { name = "pytest" },
22
- { name = "ruff" },
20
+ { name = "pytest", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" },
21
+ { name = "ruff", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" },
23
22
  ]
24
23
 
25
24
  [package.metadata]
25
+ requires-dist = [{ name = "setuptools", marker = "sys_platform == 'win32'", specifier = "<0" }]
26
26
 
27
27
  [package.metadata.requires-dev]
28
28
  dev = [
@@ -71,11 +71,10 @@ name = "pytest"
71
71
  version = "9.1.1"
72
72
  source = { registry = "https://pypi.org/simple" }
73
73
  dependencies = [
74
- { name = "colorama", marker = "sys_platform == 'win32'" },
75
- { name = "iniconfig" },
76
- { name = "packaging" },
77
- { name = "pluggy" },
78
- { name = "pygments" },
74
+ { name = "iniconfig", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" },
75
+ { name = "packaging", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" },
76
+ { name = "pluggy", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" },
77
+ { name = "pygments", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" },
79
78
  ]
80
79
  sdist = { url = "https://files.pythonhosted.org/packages/e4/47/b9efed96c114afcfa3c9d3fe98a76a1d14c74a9e266d397cf6eb64be5e01/pytest-9.1.1.tar.gz", hash = "sha256:1088fbde8f2b49d95a549a195707afa7a76a3ce9bcadc26b6d71f0ffda5fe313", size = 1636369, upload-time = "2026-06-19T10:58:32.857Z" }
81
80
  wheels = [
@@ -102,7 +101,4 @@ wheels = [
102
101
  { url = "https://files.pythonhosted.org/packages/f5/d1/5cf5909329fedb5d39d555ee818ba5cf4638e1a301b89785d34f2905bfcb/ruff-0.15.18-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:a81beadbbff2c9c245561ae3f77b16709d87f35eec650d0501679239d3449b22", size = 10693187, upload-time = "2026-06-18T18:25:08.245Z" },
103
102
  { url = "https://files.pythonhosted.org/packages/fd/44/ff6c635cf2c4f4e7b618b6640da057376baa36014695487d88aed4794268/ruff-0.15.18-py3-none-musllinux_1_2_i686.whl", hash = "sha256:2186d9e940ae332ab293623a75b5f4fe49565f449954d50a72a046683aa6b809", size = 11208721, upload-time = "2026-06-18T18:25:41.327Z" },
104
103
  { url = "https://files.pythonhosted.org/packages/88/d9/5baa2a30861adfb7022cf33c1e35b2fc18085b08c16f83eff4c7b99a5f48/ruff-0.15.18-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:5c2abf140438032bc77b2284a6c9944ecd8a19e5f1c7b52b1b8e4a0a80d19a7a", size = 11678599, upload-time = "2026-06-18T18:25:13.607Z" },
105
- { url = "https://files.pythonhosted.org/packages/c3/1a/0725a7cfdc32ff769efb96ee782bec882e16448c5d9e3be947ec4c04ce27/ruff-0.15.18-py3-none-win32.whl", hash = "sha256:02299e6e9fa5b297a3f6d5d10d7bcd655c925b028bb8b9d4588214549c6b9ec4", size = 10901903, upload-time = "2026-06-18T18:25:24.755Z" },
106
- { url = "https://files.pythonhosted.org/packages/f3/51/805d9f6fb7970505c3504794a5ec350f605361b807fef4dcf214ebd35e72/ruff-0.15.18-py3-none-win_amd64.whl", hash = "sha256:dac80dc8d26b2257dbefabed62f5d255c3937b4ccb122da1fc634794fa3578b3", size = 12041189, upload-time = "2026-06-18T18:25:17.915Z" },
107
- { url = "https://files.pythonhosted.org/packages/29/4c/67bb45e41609eb4726f1bfeb59e083cf91d14c696d4bd14c234a980be93d/ruff-0.15.18-py3-none-win_arm64.whl", hash = "sha256:b2c9257fcbd4a3e5b977a1904e6facca016bafe2edc17df24db67cfaee03b4e4", size = 11329958, upload-time = "2026-06-18T18:25:43.686Z" },
108
104
  ]
@@ -1,58 +0,0 @@
1
- [project]
2
- name = "fvpnctl"
3
- version = "0.2.0"
4
- requires-python = ">=3.11"
5
- readme = "README.md"
6
- description = "Control your FortiClient VPN from the macOS command line."
7
- license = "MIT"
8
- license-files = ["LICENSE"]
9
- authors = [{ name = "Michał Pasternak" }]
10
- keywords = ["forticlient", "fortinet", "vpn", "ipsec", "macos", "cli"]
11
- classifiers = [
12
- "Development Status :: 4 - Beta",
13
- "Environment :: Console",
14
- "Intended Audience :: System Administrators",
15
- "Operating System :: MacOS",
16
- "Programming Language :: Python :: 3",
17
- "Programming Language :: Python :: 3.11",
18
- "Programming Language :: Python :: 3.12",
19
- "Programming Language :: Python :: 3.13",
20
- "Topic :: System :: Networking",
21
- ]
22
- # Zero runtime dependencies is a hard design requirement: the tool uses only the
23
- # Python standard library (socket, json, urllib, subprocess, argparse). See the
24
- # design spec sections 2 and 8 for the rationale (no third-party attack surface,
25
- # trivially auditable, runs against any stock Python 3.11+).
26
- dependencies = []
27
-
28
- [project.urls]
29
- Homepage = "https://github.com/mpasternak/fortivpn-cli"
30
- Repository = "https://github.com/mpasternak/fortivpn-cli"
31
- Issues = "https://github.com/mpasternak/fortivpn-cli/issues"
32
-
33
- [project.scripts]
34
- fvpnctl = "fvpnctl.cli:main"
35
-
36
- [dependency-groups]
37
- # Installed by default on `uv sync` so the test suite + lint/format run out of the
38
- # box and reproducibly in CI (ruff is a real dev dep, not relied on from PATH).
39
- dev = ["pytest>=8", "ruff>=0.13"]
40
-
41
- [build-system]
42
- requires = ["hatchling"]
43
- build-backend = "hatchling.build"
44
-
45
- [tool.hatch.build.targets.wheel]
46
- packages = ["src/fvpnctl"]
47
-
48
- [tool.ruff]
49
- line-length = 100
50
- target-version = "py311"
51
-
52
- [tool.ruff.lint]
53
- # E/W pycodestyle, F pyflakes, I import sorting, UP pyupgrade, B bugbear.
54
- select = ["E", "F", "I", "UP", "B", "W"]
55
-
56
- [tool.pytest.ini_options]
57
- testpaths = ["tests"]
58
- addopts = "-q"
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes