coone-ailab-cli 0.0.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.
- ailab_eval/__init__.py +3 -0
- ailab_eval/__main__.py +4 -0
- ailab_eval/launcher.py +213 -0
- coone_ailab_cli-0.0.5.dist-info/METADATA +75 -0
- coone_ailab_cli-0.0.5.dist-info/RECORD +8 -0
- coone_ailab_cli-0.0.5.dist-info/WHEEL +5 -0
- coone_ailab_cli-0.0.5.dist-info/entry_points.txt +3 -0
- coone_ailab_cli-0.0.5.dist-info/top_level.txt +1 -0
ailab_eval/__init__.py
ADDED
ailab_eval/__main__.py
ADDED
ailab_eval/launcher.py
ADDED
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import hashlib
|
|
4
|
+
import json
|
|
5
|
+
import os
|
|
6
|
+
import platform
|
|
7
|
+
import shutil
|
|
8
|
+
import stat
|
|
9
|
+
import subprocess
|
|
10
|
+
import sys
|
|
11
|
+
import tarfile
|
|
12
|
+
import tempfile
|
|
13
|
+
import urllib.error
|
|
14
|
+
import urllib.request
|
|
15
|
+
import zipfile
|
|
16
|
+
from pathlib import Path
|
|
17
|
+
from typing import Optional
|
|
18
|
+
|
|
19
|
+
DEFAULT_REPO = "coone-ai/heimdal"
|
|
20
|
+
API_TIMEOUT_SECONDS = 20
|
|
21
|
+
DOWNLOAD_TIMEOUT_SECONDS = 120
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def _normalize_os() -> str:
|
|
25
|
+
raw = platform.system().lower()
|
|
26
|
+
if raw == "darwin":
|
|
27
|
+
return "darwin"
|
|
28
|
+
if raw == "linux":
|
|
29
|
+
return "linux"
|
|
30
|
+
if raw == "windows":
|
|
31
|
+
return "windows"
|
|
32
|
+
raise RuntimeError(f"Unsupported operating system: {raw}")
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _normalize_arch() -> str:
|
|
36
|
+
raw = platform.machine().lower()
|
|
37
|
+
if raw in {"x86_64", "amd64"}:
|
|
38
|
+
return "amd64"
|
|
39
|
+
if raw in {"arm64", "aarch64"}:
|
|
40
|
+
return "arm64"
|
|
41
|
+
raise RuntimeError(f"Unsupported architecture: {raw}")
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def _default_cache_dir() -> Path:
|
|
45
|
+
custom = os.environ.get("HEIMDAL_INSTALL_CACHE_DIR", "").strip()
|
|
46
|
+
if custom:
|
|
47
|
+
return Path(custom).expanduser()
|
|
48
|
+
|
|
49
|
+
if os.name == "nt":
|
|
50
|
+
base = os.environ.get("LOCALAPPDATA") or str(Path.home() / "AppData" / "Local")
|
|
51
|
+
return Path(base) / "coone-ailab-cli" / "cache"
|
|
52
|
+
|
|
53
|
+
xdg = os.environ.get("XDG_CACHE_HOME")
|
|
54
|
+
if xdg:
|
|
55
|
+
return Path(xdg) / "coone-ailab-cli"
|
|
56
|
+
return Path.home() / ".cache" / "coone-ailab-cli"
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def _ensure_tag_prefix(version: str) -> str:
|
|
60
|
+
version = version.strip()
|
|
61
|
+
if not version:
|
|
62
|
+
raise RuntimeError("Empty version value")
|
|
63
|
+
if version.startswith("v"):
|
|
64
|
+
return version
|
|
65
|
+
return f"v{version}"
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def _fetch_latest_tag(repo: str) -> str:
|
|
69
|
+
url = f"https://api.github.com/repos/{repo}/releases/latest"
|
|
70
|
+
req = urllib.request.Request(
|
|
71
|
+
url,
|
|
72
|
+
headers={
|
|
73
|
+
"Accept": "application/vnd.github+json",
|
|
74
|
+
"User-Agent": "coone-ailab-cli-bootstrap",
|
|
75
|
+
},
|
|
76
|
+
)
|
|
77
|
+
with urllib.request.urlopen(req, timeout=API_TIMEOUT_SECONDS) as resp:
|
|
78
|
+
payload = json.loads(resp.read().decode("utf-8"))
|
|
79
|
+
tag = str(payload.get("tag_name", "")).strip()
|
|
80
|
+
if not tag:
|
|
81
|
+
raise RuntimeError("GitHub latest release payload does not contain tag_name")
|
|
82
|
+
return _ensure_tag_prefix(tag)
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def _resolve_tag(repo: str) -> str:
|
|
86
|
+
env_version = os.environ.get("HEIMDAL_VERSION", "").strip()
|
|
87
|
+
if env_version:
|
|
88
|
+
return _ensure_tag_prefix(env_version)
|
|
89
|
+
return _fetch_latest_tag(repo)
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def _archive_name(tag: str, os_name: str, arch: str) -> str:
|
|
93
|
+
ext = "zip" if os_name == "windows" else "tar.gz"
|
|
94
|
+
return f"heimdal_{tag}_{os_name}_{arch}.{ext}"
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def _download(url: str, dest: Path, timeout: int) -> None:
|
|
98
|
+
req = urllib.request.Request(url, headers={"User-Agent": "coone-ailab-cli-bootstrap"})
|
|
99
|
+
with urllib.request.urlopen(req, timeout=timeout) as resp, dest.open("wb") as out:
|
|
100
|
+
shutil.copyfileobj(resp, out)
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def _sha256_file(path: Path) -> str:
|
|
104
|
+
digest = hashlib.sha256()
|
|
105
|
+
with path.open("rb") as f:
|
|
106
|
+
for chunk in iter(lambda: f.read(1024 * 1024), b""):
|
|
107
|
+
digest.update(chunk)
|
|
108
|
+
return digest.hexdigest()
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def _parse_checksum_file(checksum_file: Path, archive_name: str) -> Optional[str]:
|
|
112
|
+
for line in checksum_file.read_text(encoding="utf-8", errors="replace").splitlines():
|
|
113
|
+
line = line.strip()
|
|
114
|
+
if not line or " " not in line:
|
|
115
|
+
continue
|
|
116
|
+
checksum, filename = line.split(" ", 1)
|
|
117
|
+
if Path(filename.strip()).name == archive_name:
|
|
118
|
+
return checksum.strip().lower()
|
|
119
|
+
return None
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def _verify_checksum_if_available(repo: str, tag: str, archive_name: str, archive_path: Path) -> None:
|
|
123
|
+
checksum_name = f"heimdal_{tag}_checksums.txt"
|
|
124
|
+
checksum_url = f"https://github.com/{repo}/releases/download/{tag}/{checksum_name}"
|
|
125
|
+
with tempfile.TemporaryDirectory(prefix="coone-ailab-cli-checksum-") as td:
|
|
126
|
+
checksum_path = Path(td) / checksum_name
|
|
127
|
+
try:
|
|
128
|
+
_download(checksum_url, checksum_path, timeout=API_TIMEOUT_SECONDS)
|
|
129
|
+
except Exception:
|
|
130
|
+
return
|
|
131
|
+
|
|
132
|
+
expected = _parse_checksum_file(checksum_path, archive_name)
|
|
133
|
+
if not expected:
|
|
134
|
+
return
|
|
135
|
+
|
|
136
|
+
actual = _sha256_file(archive_path).lower()
|
|
137
|
+
if actual != expected:
|
|
138
|
+
raise RuntimeError(
|
|
139
|
+
f"Checksum verification failed for {archive_name}: expected {expected}, got {actual}"
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def _extract_binary(archive_path: Path, target_path: Path, os_name: str) -> None:
|
|
144
|
+
binary_name = "heimdal.exe" if os_name == "windows" else "heimdal"
|
|
145
|
+
with tempfile.TemporaryDirectory(prefix="coone-ailab-cli-extract-") as td:
|
|
146
|
+
temp_dir = Path(td)
|
|
147
|
+
if archive_path.suffix == ".zip":
|
|
148
|
+
with zipfile.ZipFile(archive_path, "r") as zf:
|
|
149
|
+
zf.extractall(temp_dir)
|
|
150
|
+
else:
|
|
151
|
+
with tarfile.open(archive_path, "r:gz") as tf:
|
|
152
|
+
tf.extractall(temp_dir)
|
|
153
|
+
|
|
154
|
+
candidates = list(temp_dir.rglob(binary_name))
|
|
155
|
+
if not candidates:
|
|
156
|
+
raise RuntimeError(f"Could not find {binary_name} in downloaded archive")
|
|
157
|
+
|
|
158
|
+
source = candidates[0]
|
|
159
|
+
target_path.parent.mkdir(parents=True, exist_ok=True)
|
|
160
|
+
shutil.move(str(source), str(target_path))
|
|
161
|
+
|
|
162
|
+
if os_name != "windows":
|
|
163
|
+
current_mode = target_path.stat().st_mode
|
|
164
|
+
target_path.chmod(current_mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH)
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def _ensure_binary(cache_dir: Path, repo: str, tag: str, os_name: str, arch: str) -> Path:
|
|
168
|
+
binary_name = "heimdal.exe" if os_name == "windows" else "heimdal"
|
|
169
|
+
bin_dir = cache_dir / "binaries" / tag / f"{os_name}-{arch}"
|
|
170
|
+
binary_path = bin_dir / binary_name
|
|
171
|
+
if binary_path.exists():
|
|
172
|
+
return binary_path
|
|
173
|
+
|
|
174
|
+
archive = _archive_name(tag, os_name, arch)
|
|
175
|
+
download_url = f"https://github.com/{repo}/releases/download/{tag}/{archive}"
|
|
176
|
+
bin_dir.mkdir(parents=True, exist_ok=True)
|
|
177
|
+
|
|
178
|
+
with tempfile.TemporaryDirectory(prefix="coone-ailab-cli-download-") as td:
|
|
179
|
+
archive_path = Path(td) / archive
|
|
180
|
+
try:
|
|
181
|
+
_download(download_url, archive_path, timeout=DOWNLOAD_TIMEOUT_SECONDS)
|
|
182
|
+
except urllib.error.HTTPError as exc:
|
|
183
|
+
raise RuntimeError(
|
|
184
|
+
f"Failed to download release asset ({exc.code}): {download_url}"
|
|
185
|
+
) from exc
|
|
186
|
+
except urllib.error.URLError as exc:
|
|
187
|
+
raise RuntimeError(f"Failed to connect while downloading: {download_url}") from exc
|
|
188
|
+
|
|
189
|
+
_verify_checksum_if_available(repo, tag, archive, archive_path)
|
|
190
|
+
_extract_binary(archive_path, binary_path, os_name)
|
|
191
|
+
|
|
192
|
+
return binary_path
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
def _run_binary(binary_path: Path, argv: list[str]) -> int:
|
|
196
|
+
cmd = [str(binary_path), *argv]
|
|
197
|
+
proc = subprocess.run(cmd)
|
|
198
|
+
return int(proc.returncode)
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
def main() -> None:
|
|
202
|
+
repo = os.environ.get("HEIMDAL_REPO", DEFAULT_REPO).strip() or DEFAULT_REPO
|
|
203
|
+
cache_dir = _default_cache_dir()
|
|
204
|
+
try:
|
|
205
|
+
os_name = _normalize_os()
|
|
206
|
+
arch = _normalize_arch()
|
|
207
|
+
tag = _resolve_tag(repo)
|
|
208
|
+
binary = _ensure_binary(cache_dir, repo, tag, os_name, arch)
|
|
209
|
+
rc = _run_binary(binary, sys.argv[1:])
|
|
210
|
+
except Exception as exc:
|
|
211
|
+
print(f"heimdal bootstrap error: {exc}", file=sys.stderr)
|
|
212
|
+
sys.exit(1)
|
|
213
|
+
sys.exit(rc)
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: coone-ailab-cli
|
|
3
|
+
Version: 0.0.5
|
|
4
|
+
Summary: Python bootstrap launcher for Heimdal CLI
|
|
5
|
+
Author: Co-one
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/coone-ai/heimdal
|
|
8
|
+
Project-URL: Source, https://github.com/coone-ai/heimdal
|
|
9
|
+
Project-URL: Issues, https://github.com/coone-ai/heimdal/issues
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Operating System :: OS Independent
|
|
14
|
+
Classifier: Environment :: Console
|
|
15
|
+
Requires-Python: >=3.9
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
|
|
18
|
+
# coone-ailab-cli
|
|
19
|
+
|
|
20
|
+
`coone-ailab-cli` is a lightweight Python bootstrap package for Heimdal CLI.
|
|
21
|
+
|
|
22
|
+
After installation, running `heimdal` downloads the matching Heimdal binary from
|
|
23
|
+
GitHub Releases (if needed), stores it in a local cache, and forwards all
|
|
24
|
+
arguments to the binary.
|
|
25
|
+
|
|
26
|
+
## Install
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
pip install coone-ailab-cli
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Recommended
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
pipx install coone-ailab-cli
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Environment Variables
|
|
39
|
+
|
|
40
|
+
- `HEIMDAL_REPO`: GitHub repository in `owner/name` format.
|
|
41
|
+
Default: `coone-ai/heimdal`
|
|
42
|
+
- `HEIMDAL_VERSION`: Release tag to pin (for example `v0.0.1`).
|
|
43
|
+
Default: latest release
|
|
44
|
+
- `HEIMDAL_INSTALL_CACHE_DIR`: Cache directory for downloaded binaries
|
|
45
|
+
|
|
46
|
+
## Publish
|
|
47
|
+
|
|
48
|
+
Quick release (version bump + commit + tag + push):
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
./scripts/release.sh 0.0.2
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Local only (no push):
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
./scripts/release.sh 0.0.2 --no-push
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Manual publish:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
./scripts/publish.sh
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
CI publishing is also configured at:
|
|
67
|
+
|
|
68
|
+
- `.github/workflows/release.yml`
|
|
69
|
+
|
|
70
|
+
Tag format:
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
git tag v0.0.1
|
|
74
|
+
git push origin v0.0.1
|
|
75
|
+
```
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
ailab_eval/__init__.py,sha256=8VOm8ou-5WyjwJ8ypdRd5yC_Y02tb-dXMJ1Jtwg8hv8,49
|
|
2
|
+
ailab_eval/__main__.py,sha256=YCgbcXzdcyqg0_oLgtLG-AorilbdQfnpndSqKjlVyPY,66
|
|
3
|
+
ailab_eval/launcher.py,sha256=sGFSvp3VpWge3AKcJTh3KA4aNAb3Hw6f1Rt3y3PnpnM,7240
|
|
4
|
+
coone_ailab_cli-0.0.5.dist-info/METADATA,sha256=WM75zmYZHNQ2Y2eOs937y11ZPSPIrkd_coZlDyR7XIY,1655
|
|
5
|
+
coone_ailab_cli-0.0.5.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
|
|
6
|
+
coone_ailab_cli-0.0.5.dist-info/entry_points.txt,sha256=incn-mZBHHEHACpQzaaXpb8expwq71APwaZ3UaJb8o4,86
|
|
7
|
+
coone_ailab_cli-0.0.5.dist-info/top_level.txt,sha256=QRi4OG64_4yH00u4o9W7SRzOOFSM295VF0-iE2um-Sw,11
|
|
8
|
+
coone_ailab_cli-0.0.5.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
ailab_eval
|