pcf-toolkit 0.2.5__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.
- pcf_toolkit/__init__.py +6 -0
- pcf_toolkit/cli.py +738 -0
- pcf_toolkit/cli_helpers.py +62 -0
- pcf_toolkit/data/__init__.py +1 -0
- pcf_toolkit/data/manifest.schema.json +1097 -0
- pcf_toolkit/data/schema_snapshot.json +2377 -0
- pcf_toolkit/data/spec_raw.json +2877 -0
- pcf_toolkit/io.py +65 -0
- pcf_toolkit/json_schema.py +30 -0
- pcf_toolkit/models.py +384 -0
- pcf_toolkit/proxy/__init__.py +1 -0
- pcf_toolkit/proxy/addons/__init__.py +1 -0
- pcf_toolkit/proxy/addons/redirect_bundle.py +70 -0
- pcf_toolkit/proxy/browser.py +157 -0
- pcf_toolkit/proxy/cli.py +1570 -0
- pcf_toolkit/proxy/config.py +310 -0
- pcf_toolkit/proxy/doctor.py +279 -0
- pcf_toolkit/proxy/mitm.py +206 -0
- pcf_toolkit/proxy/server.py +50 -0
- pcf_toolkit/py.typed +1 -0
- pcf_toolkit/rich_help.py +173 -0
- pcf_toolkit/schema_snapshot.py +47 -0
- pcf_toolkit/types.py +95 -0
- pcf_toolkit/xml.py +484 -0
- pcf_toolkit/xml_import.py +548 -0
- pcf_toolkit-0.2.5.dist-info/METADATA +494 -0
- pcf_toolkit-0.2.5.dist-info/RECORD +31 -0
- pcf_toolkit-0.2.5.dist-info/WHEEL +5 -0
- pcf_toolkit-0.2.5.dist-info/entry_points.txt +2 -0
- pcf_toolkit-0.2.5.dist-info/licenses/LICENSE.md +183 -0
- pcf_toolkit-0.2.5.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
"""Browser discovery and launch helpers."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
import shutil
|
|
7
|
+
import subprocess
|
|
8
|
+
import sys
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def find_browser_binary(browser: str | None, explicit_path: Path | None) -> Path | None:
|
|
13
|
+
"""Finds a browser executable binary.
|
|
14
|
+
|
|
15
|
+
Args:
|
|
16
|
+
browser: Browser preference ("chrome", "edge", or None for auto).
|
|
17
|
+
explicit_path: Explicit path to browser binary.
|
|
18
|
+
|
|
19
|
+
Returns:
|
|
20
|
+
Path to browser executable, or None if not found.
|
|
21
|
+
"""
|
|
22
|
+
if explicit_path and explicit_path.exists():
|
|
23
|
+
return explicit_path
|
|
24
|
+
|
|
25
|
+
if browser:
|
|
26
|
+
normalized = browser.lower()
|
|
27
|
+
else:
|
|
28
|
+
normalized = "auto"
|
|
29
|
+
|
|
30
|
+
if normalized in {"chrome", "google-chrome"}:
|
|
31
|
+
return _find_chrome()
|
|
32
|
+
if normalized in {"edge", "microsoft-edge"}:
|
|
33
|
+
return _find_edge()
|
|
34
|
+
if normalized in {"auto", ""}:
|
|
35
|
+
return _find_chrome() or _find_edge()
|
|
36
|
+
|
|
37
|
+
resolved = shutil.which(browser)
|
|
38
|
+
if resolved:
|
|
39
|
+
return Path(resolved)
|
|
40
|
+
|
|
41
|
+
return None
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def launch_browser(
|
|
45
|
+
binary: Path,
|
|
46
|
+
crm_url: str,
|
|
47
|
+
proxy_host: str,
|
|
48
|
+
proxy_port: int,
|
|
49
|
+
profile_dir: Path,
|
|
50
|
+
) -> subprocess.Popen:
|
|
51
|
+
"""Launches a browser with proxy configuration.
|
|
52
|
+
|
|
53
|
+
Args:
|
|
54
|
+
binary: Path to browser executable.
|
|
55
|
+
crm_url: CRM URL to open.
|
|
56
|
+
proxy_host: Proxy server hostname.
|
|
57
|
+
proxy_port: Proxy server port.
|
|
58
|
+
profile_dir: Directory for browser profile data.
|
|
59
|
+
|
|
60
|
+
Returns:
|
|
61
|
+
Popen instance for the browser process.
|
|
62
|
+
"""
|
|
63
|
+
profile_dir.mkdir(parents=True, exist_ok=True)
|
|
64
|
+
args = [
|
|
65
|
+
str(binary),
|
|
66
|
+
f"--proxy-server={proxy_host}:{proxy_port}",
|
|
67
|
+
f"--user-data-dir={profile_dir}",
|
|
68
|
+
"--no-first-run",
|
|
69
|
+
"--no-default-browser-check",
|
|
70
|
+
"--new-window",
|
|
71
|
+
crm_url,
|
|
72
|
+
]
|
|
73
|
+
creationflags = 0
|
|
74
|
+
if os.name == "nt":
|
|
75
|
+
creationflags = subprocess.CREATE_NEW_PROCESS_GROUP
|
|
76
|
+
return subprocess.Popen(
|
|
77
|
+
args,
|
|
78
|
+
stdout=subprocess.DEVNULL,
|
|
79
|
+
stderr=subprocess.DEVNULL,
|
|
80
|
+
start_new_session=os.name != "nt",
|
|
81
|
+
creationflags=creationflags,
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def _find_chrome() -> Path | None:
|
|
86
|
+
"""Finds Google Chrome executable.
|
|
87
|
+
|
|
88
|
+
Returns:
|
|
89
|
+
Path to Chrome executable, or None if not found.
|
|
90
|
+
"""
|
|
91
|
+
candidates = []
|
|
92
|
+
if os.name == "nt":
|
|
93
|
+
program_files = os.environ.get("PROGRAMFILES")
|
|
94
|
+
program_files_x86 = os.environ.get("PROGRAMFILES(X86)")
|
|
95
|
+
local_appdata = os.environ.get("LOCALAPPDATA")
|
|
96
|
+
for base in (program_files, program_files_x86, local_appdata):
|
|
97
|
+
if base:
|
|
98
|
+
candidates.append(Path(base) / "Google" / "Chrome" / "Application" / "chrome.exe")
|
|
99
|
+
elif sys.platform == "darwin":
|
|
100
|
+
candidates.extend(
|
|
101
|
+
[
|
|
102
|
+
Path("/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"),
|
|
103
|
+
Path.home() / "Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
|
|
104
|
+
]
|
|
105
|
+
)
|
|
106
|
+
else:
|
|
107
|
+
for name in ("google-chrome", "google-chrome-stable", "chromium", "chromium-browser"):
|
|
108
|
+
resolved = shutil.which(name)
|
|
109
|
+
if resolved:
|
|
110
|
+
return Path(resolved)
|
|
111
|
+
|
|
112
|
+
return _first_existing(candidates)
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def _find_edge() -> Path | None:
|
|
116
|
+
"""Finds Microsoft Edge executable.
|
|
117
|
+
|
|
118
|
+
Returns:
|
|
119
|
+
Path to Edge executable, or None if not found.
|
|
120
|
+
"""
|
|
121
|
+
candidates = []
|
|
122
|
+
if os.name == "nt":
|
|
123
|
+
program_files = os.environ.get("PROGRAMFILES")
|
|
124
|
+
program_files_x86 = os.environ.get("PROGRAMFILES(X86)")
|
|
125
|
+
local_appdata = os.environ.get("LOCALAPPDATA")
|
|
126
|
+
for base in (program_files, program_files_x86, local_appdata):
|
|
127
|
+
if base:
|
|
128
|
+
candidates.append(Path(base) / "Microsoft" / "Edge" / "Application" / "msedge.exe")
|
|
129
|
+
elif sys.platform == "darwin":
|
|
130
|
+
candidates.extend(
|
|
131
|
+
[
|
|
132
|
+
Path("/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge"),
|
|
133
|
+
Path.home() / "Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge",
|
|
134
|
+
]
|
|
135
|
+
)
|
|
136
|
+
else:
|
|
137
|
+
for name in ("microsoft-edge", "microsoft-edge-stable"):
|
|
138
|
+
resolved = shutil.which(name)
|
|
139
|
+
if resolved:
|
|
140
|
+
return Path(resolved)
|
|
141
|
+
|
|
142
|
+
return _first_existing(candidates)
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def _first_existing(paths: list[Path]) -> Path | None:
|
|
146
|
+
"""Returns the first path that exists.
|
|
147
|
+
|
|
148
|
+
Args:
|
|
149
|
+
paths: List of paths to check.
|
|
150
|
+
|
|
151
|
+
Returns:
|
|
152
|
+
First existing path, or None if none exist.
|
|
153
|
+
"""
|
|
154
|
+
for path in paths:
|
|
155
|
+
if path.exists():
|
|
156
|
+
return path
|
|
157
|
+
return None
|