plugin-kit-ai 1.0.4__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,33 @@
1
+ Metadata-Version: 2.4
2
+ Name: plugin-kit-ai
3
+ Version: 1.0.4
4
+ Summary: Thin Python launcher for the plugin-kit-ai CLI
5
+ Author: plugin-kit-ai
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/777genius/plugin-kit-ai
8
+ Project-URL: Repository, https://github.com/777genius/plugin-kit-ai
9
+ Classifier: Environment :: Console
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: License :: OSI Approved :: MIT License
12
+ Classifier: Operating System :: MacOS
13
+ Classifier: Operating System :: Microsoft :: Windows
14
+ Classifier: Operating System :: POSIX :: Linux
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3 :: Only
17
+ Classifier: Topic :: Software Development :: Build Tools
18
+ Requires-Python: >=3.9
19
+ Description-Content-Type: text/markdown
20
+
21
+ # plugin-kit-ai (PyPI wrapper)
22
+
23
+ Thin Python launcher for the `plugin-kit-ai` CLI.
24
+
25
+ It downloads the matching published GitHub Releases binary, verifies
26
+ `checksums.txt`, caches the binary locally, and executes it.
27
+
28
+ Primary user path:
29
+
30
+ ```bash
31
+ pipx install plugin-kit-ai
32
+ plugin-kit-ai version
33
+ ```
@@ -0,0 +1,13 @@
1
+ # plugin-kit-ai (PyPI wrapper)
2
+
3
+ Thin Python launcher for the `plugin-kit-ai` CLI.
4
+
5
+ It downloads the matching published GitHub Releases binary, verifies
6
+ `checksums.txt`, caches the binary locally, and executes it.
7
+
8
+ Primary user path:
9
+
10
+ ```bash
11
+ pipx install plugin-kit-ai
12
+ plugin-kit-ai version
13
+ ```
@@ -0,0 +1,39 @@
1
+ [build-system]
2
+ requires = ["setuptools>=69", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "plugin-kit-ai"
7
+ description = "Thin Python launcher for the plugin-kit-ai CLI"
8
+ readme = "README.md"
9
+ requires-python = ">=3.9"
10
+ license = { text = "MIT" }
11
+ authors = [{ name = "plugin-kit-ai" }]
12
+ dynamic = ["version"]
13
+ classifiers = [
14
+ "Environment :: Console",
15
+ "Intended Audience :: Developers",
16
+ "License :: OSI Approved :: MIT License",
17
+ "Operating System :: MacOS",
18
+ "Operating System :: Microsoft :: Windows",
19
+ "Operating System :: POSIX :: Linux",
20
+ "Programming Language :: Python :: 3",
21
+ "Programming Language :: Python :: 3 :: Only",
22
+ "Topic :: Software Development :: Build Tools",
23
+ ]
24
+
25
+ [project.scripts]
26
+ plugin-kit-ai = "plugin_kit_ai.cli:main"
27
+
28
+ [project.urls]
29
+ Homepage = "https://github.com/777genius/plugin-kit-ai"
30
+ Repository = "https://github.com/777genius/plugin-kit-ai"
31
+
32
+ [tool.setuptools]
33
+ package-dir = { "" = "src" }
34
+
35
+ [tool.setuptools.packages.find]
36
+ where = ["src"]
37
+
38
+ [tool.setuptools.dynamic]
39
+ version = { attr = "plugin_kit_ai.__version__" }
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,3 @@
1
+ """plugin-kit-ai Python wrapper package."""
2
+
3
+ __version__ = "1.0.4"
@@ -0,0 +1,19 @@
1
+ """Console entrypoint for the plugin-kit-ai Python wrapper."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import sys
6
+
7
+ from .install import format_install_error, run_binary
8
+
9
+
10
+ def main() -> None:
11
+ try:
12
+ raise SystemExit(run_binary())
13
+ except Exception as err: # pragma: no cover - exercised through integration tests
14
+ sys.stderr.write(format_install_error(err) + "\n")
15
+ raise SystemExit(1)
16
+
17
+
18
+ if __name__ == "__main__":
19
+ main()
@@ -0,0 +1,215 @@
1
+ """Install and execute the plugin-kit-ai CLI binary from GitHub Releases."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import hashlib
6
+ import json
7
+ import os
8
+ import shutil
9
+ import stat
10
+ import subprocess
11
+ import sys
12
+ import tarfile
13
+ import tempfile
14
+ from pathlib import Path
15
+ from typing import Dict, Iterable, Optional
16
+ from urllib.error import HTTPError, URLError
17
+ from urllib.request import Request, urlopen
18
+
19
+ from . import __version__
20
+ from .platform import asset_name_for_version, detect_platform
21
+
22
+ DEFAULT_REPOSITORY = "777genius/plugin-kit-ai"
23
+ DEFAULT_API_BASE = "https://api.github.com"
24
+ PLACEHOLDER_VERSION = "0.0.0-development"
25
+
26
+
27
+ def normalize_tag(raw: str) -> str:
28
+ value = str(raw or "").strip()
29
+ if not value or value == "latest":
30
+ return ""
31
+ return value if value.startswith("v") else f"v{value}"
32
+
33
+
34
+ def derive_release_base(api_base: str, override: str) -> str:
35
+ if override and override.strip():
36
+ return override.strip().rstrip("/")
37
+ trimmed = str(api_base or DEFAULT_API_BASE).strip().rstrip("/")
38
+ if trimmed in {"https://api.github.com", "http://api.github.com"}:
39
+ return "https://github.com"
40
+ if trimmed.endswith("/api/v3"):
41
+ return trimmed[: -len("/api/v3")]
42
+ if trimmed.endswith("/api"):
43
+ return trimmed[: -len("/api")]
44
+ return trimmed
45
+
46
+
47
+ def resolve_requested_tag() -> str:
48
+ env_version = normalize_tag(os.environ.get("PLUGIN_KIT_AI_VERSION", ""))
49
+ if env_version:
50
+ return env_version
51
+ if __version__ and __version__ != PLACEHOLDER_VERSION:
52
+ return normalize_tag(__version__)
53
+ return ""
54
+
55
+
56
+ def request_headers(accept_json: bool) -> Dict[str, str]:
57
+ headers: Dict[str, str] = {}
58
+ token = os.environ.get("GITHUB_TOKEN", "").strip()
59
+ if token:
60
+ headers["Authorization"] = f"Bearer {token}"
61
+ if accept_json:
62
+ headers["Accept"] = "application/vnd.github+json"
63
+ return headers
64
+
65
+
66
+ def fetch_bytes(url: str, accept_json: bool = False) -> bytes:
67
+ req = Request(url, headers=request_headers(accept_json))
68
+ try:
69
+ with urlopen(req) as resp:
70
+ return resp.read()
71
+ except (HTTPError, URLError) as exc:
72
+ raise RuntimeError(f"request failed for {url}: {exc}") from exc
73
+
74
+
75
+ def fetch_text(url: str, accept_json: bool = False) -> str:
76
+ return fetch_bytes(url, accept_json=accept_json).decode("utf-8")
77
+
78
+
79
+ def latest_tag(api_base: str, repository: str) -> str:
80
+ clean_base = str(api_base or DEFAULT_API_BASE).strip().rstrip("/")
81
+ payload = json.loads(fetch_text(f"{clean_base}/repos/{repository}/releases/latest", accept_json=True))
82
+ tag_name = str(payload.get("tag_name", "")).strip()
83
+ if not tag_name:
84
+ raise RuntimeError(f"could not resolve latest release tag from {clean_base}")
85
+ return normalize_tag(tag_name)
86
+
87
+
88
+ def parse_checksums(text: str) -> Dict[str, str]:
89
+ out: Dict[str, str] = {}
90
+ for raw_line in str(text or "").splitlines():
91
+ line = raw_line.strip()
92
+ if not line:
93
+ continue
94
+ fields = line.split()
95
+ if len(fields) < 2:
96
+ raise RuntimeError(f'invalid checksums.txt line "{line}"')
97
+ checksum = fields[0].strip()
98
+ name = fields[-1].lstrip("*").strip()
99
+ out[name] = checksum
100
+ return out
101
+
102
+
103
+ def sha256_bytes(body: bytes) -> str:
104
+ return hashlib.sha256(body).hexdigest()
105
+
106
+
107
+ def default_cache_root() -> Path:
108
+ override = os.environ.get("PLUGIN_KIT_AI_CACHE_DIR", "").strip()
109
+ if override:
110
+ return Path(override)
111
+ if sys.platform == "darwin":
112
+ return Path.home() / "Library" / "Caches" / "plugin-kit-ai"
113
+ if sys.platform == "win32":
114
+ base = os.environ.get("LOCALAPPDATA") or str(Path.home() / "AppData" / "Local")
115
+ return Path(base) / "plugin-kit-ai"
116
+ return Path(os.environ.get("XDG_CACHE_HOME", str(Path.home() / ".cache"))) / "plugin-kit-ai"
117
+
118
+
119
+ def extract_binary(archive_path: Path, wanted_name: str, target_path: Path) -> None:
120
+ with tarfile.open(archive_path, mode="r:gz") as archive:
121
+ for member in archive.getmembers():
122
+ if not member.isfile():
123
+ continue
124
+ if Path(member.name).name != wanted_name:
125
+ continue
126
+ extracted = archive.extractfile(member)
127
+ if extracted is None:
128
+ continue
129
+ target_path.parent.mkdir(parents=True, exist_ok=True)
130
+ with extracted, target_path.open("wb") as out:
131
+ shutil.copyfileobj(extracted, out)
132
+ return
133
+ raise RuntimeError(f"archive does not contain {wanted_name} at archive root")
134
+
135
+
136
+ def ensure_installed(*, quiet: bool = False) -> Dict[str, str]:
137
+ repository = os.environ.get("PLUGIN_KIT_AI_REPOSITORY", DEFAULT_REPOSITORY)
138
+ api_base = os.environ.get("GITHUB_API_BASE", DEFAULT_API_BASE)
139
+ release_base = derive_release_base(api_base, os.environ.get("PLUGIN_KIT_AI_RELEASE_BASE_URL", ""))
140
+ platform_info = detect_platform()
141
+
142
+ tag = resolve_requested_tag()
143
+ if not tag:
144
+ tag = latest_tag(api_base, repository)
145
+ version = tag[1:]
146
+ asset_name = asset_name_for_version(version, platform_info)
147
+
148
+ cache_root = default_cache_root()
149
+ installed_binary = cache_root / tag / platform_info.binary_name
150
+ if installed_binary.exists():
151
+ return {
152
+ "tag": tag,
153
+ "version": version,
154
+ "asset_name": asset_name,
155
+ "installed_binary": str(installed_binary),
156
+ "repository": repository,
157
+ }
158
+
159
+ download_base = f"{release_base}/{repository}/releases/download/{tag}"
160
+ checksums = parse_checksums(fetch_text(f"{download_base}/checksums.txt"))
161
+ if asset_name not in checksums:
162
+ raise RuntimeError(f"checksums.txt missing asset {asset_name}")
163
+
164
+ archive = fetch_bytes(f"{download_base}/{asset_name}")
165
+ expected_sum = checksums[asset_name]
166
+ actual_sum = sha256_bytes(archive)
167
+ if actual_sum != expected_sum:
168
+ raise RuntimeError(f"checksum mismatch for {asset_name}")
169
+
170
+ with tempfile.TemporaryDirectory(prefix="plugin-kit-ai-pypi-") as tmpdir:
171
+ archive_path = Path(tmpdir) / asset_name
172
+ archive_path.write_bytes(archive)
173
+ extract_binary(archive_path, platform_info.binary_name, installed_binary)
174
+
175
+ if platform_info.os_name != "windows":
176
+ current_mode = installed_binary.stat().st_mode
177
+ installed_binary.chmod(current_mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH)
178
+
179
+ if not quiet:
180
+ lines = [
181
+ "Installed plugin-kit-ai PyPI wrapper binary",
182
+ f"Version: {tag}",
183
+ f"Repository: {repository}",
184
+ f"Asset: {asset_name}",
185
+ f"Installed path: {installed_binary}",
186
+ "Checksum: verified via checksums.txt",
187
+ ]
188
+ sys.stdout.write(os.linesep.join(lines) + os.linesep)
189
+
190
+ return {
191
+ "tag": tag,
192
+ "version": version,
193
+ "asset_name": asset_name,
194
+ "installed_binary": str(installed_binary),
195
+ "repository": repository,
196
+ }
197
+
198
+
199
+ def format_install_error(err: Exception) -> str:
200
+ return os.linesep.join(
201
+ [
202
+ f"plugin-kit-ai PyPI bootstrap: {err}",
203
+ "Fallbacks:",
204
+ "- Homebrew: brew install 777genius/homebrew-plugin-kit-ai/plugin-kit-ai",
205
+ "- npm: npm i -g plugin-kit-ai",
206
+ "- Verified script: curl -fsSL https://raw.githubusercontent.com/777genius/plugin-kit-ai/main/scripts/install.sh | sh",
207
+ ]
208
+ )
209
+
210
+
211
+ def run_binary(argv: Optional[Iterable[str]] = None) -> int:
212
+ install = ensure_installed(quiet=True)
213
+ args = [install["installed_binary"], *(list(argv) if argv is not None else sys.argv[1:])]
214
+ completed = subprocess.run(args, check=False)
215
+ return completed.returncode
@@ -0,0 +1,41 @@
1
+ """Platform helpers for the plugin-kit-ai Python wrapper."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import platform
6
+ from dataclasses import dataclass
7
+
8
+
9
+ @dataclass(frozen=True)
10
+ class PlatformInfo:
11
+ os_name: str
12
+ arch_name: str
13
+ binary_name: str
14
+
15
+
16
+ def detect_platform() -> PlatformInfo:
17
+ system = platform.system().lower()
18
+ machine = platform.machine().lower()
19
+
20
+ if system == "darwin":
21
+ os_name = "darwin"
22
+ elif system == "linux":
23
+ os_name = "linux"
24
+ elif system == "windows":
25
+ os_name = "windows"
26
+ else:
27
+ raise RuntimeError(f"unsupported OS {platform.system()}")
28
+
29
+ if machine in {"x86_64", "amd64"}:
30
+ arch_name = "amd64"
31
+ elif machine in {"arm64", "aarch64"}:
32
+ arch_name = "arm64"
33
+ else:
34
+ raise RuntimeError(f"unsupported architecture {platform.machine()}")
35
+
36
+ binary_name = "plugin-kit-ai.exe" if os_name == "windows" else "plugin-kit-ai"
37
+ return PlatformInfo(os_name=os_name, arch_name=arch_name, binary_name=binary_name)
38
+
39
+
40
+ def asset_name_for_version(version: str, platform_info: PlatformInfo) -> str:
41
+ return f"plugin-kit-ai_{version}_{platform_info.os_name}_{platform_info.arch_name}.tar.gz"
@@ -0,0 +1,33 @@
1
+ Metadata-Version: 2.4
2
+ Name: plugin-kit-ai
3
+ Version: 1.0.4
4
+ Summary: Thin Python launcher for the plugin-kit-ai CLI
5
+ Author: plugin-kit-ai
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/777genius/plugin-kit-ai
8
+ Project-URL: Repository, https://github.com/777genius/plugin-kit-ai
9
+ Classifier: Environment :: Console
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: License :: OSI Approved :: MIT License
12
+ Classifier: Operating System :: MacOS
13
+ Classifier: Operating System :: Microsoft :: Windows
14
+ Classifier: Operating System :: POSIX :: Linux
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3 :: Only
17
+ Classifier: Topic :: Software Development :: Build Tools
18
+ Requires-Python: >=3.9
19
+ Description-Content-Type: text/markdown
20
+
21
+ # plugin-kit-ai (PyPI wrapper)
22
+
23
+ Thin Python launcher for the `plugin-kit-ai` CLI.
24
+
25
+ It downloads the matching published GitHub Releases binary, verifies
26
+ `checksums.txt`, caches the binary locally, and executes it.
27
+
28
+ Primary user path:
29
+
30
+ ```bash
31
+ pipx install plugin-kit-ai
32
+ plugin-kit-ai version
33
+ ```
@@ -0,0 +1,11 @@
1
+ README.md
2
+ pyproject.toml
3
+ src/plugin_kit_ai/__init__.py
4
+ src/plugin_kit_ai/cli.py
5
+ src/plugin_kit_ai/install.py
6
+ src/plugin_kit_ai/platform.py
7
+ src/plugin_kit_ai.egg-info/PKG-INFO
8
+ src/plugin_kit_ai.egg-info/SOURCES.txt
9
+ src/plugin_kit_ai.egg-info/dependency_links.txt
10
+ src/plugin_kit_ai.egg-info/entry_points.txt
11
+ src/plugin_kit_ai.egg-info/top_level.txt
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ plugin-kit-ai = plugin_kit_ai.cli:main
@@ -0,0 +1 @@
1
+ plugin_kit_ai