ottili-coder 1.0.0__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.
ottili_coder/__init__.py
ADDED
ottili_coder/cli.py
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import platform
|
|
5
|
+
import shutil
|
|
6
|
+
import stat
|
|
7
|
+
import subprocess
|
|
8
|
+
import sys
|
|
9
|
+
import tempfile
|
|
10
|
+
import urllib.error
|
|
11
|
+
import urllib.request
|
|
12
|
+
import zipfile
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
|
|
15
|
+
from . import __version__
|
|
16
|
+
|
|
17
|
+
DEFAULT_RELEASE_REPO = "Ottili-ONE/coder-cli"
|
|
18
|
+
DEFAULT_VERSION = __version__
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def _release_repo() -> str:
|
|
22
|
+
return os.environ.get("OTTILI_CODER_RELEASE_REPO", DEFAULT_RELEASE_REPO)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _release_version() -> str:
|
|
26
|
+
return os.environ.get("OTTILI_CODER_VERSION", DEFAULT_VERSION).lstrip("v")
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _platform_key() -> tuple[str, str]:
|
|
30
|
+
system = platform.system().lower()
|
|
31
|
+
machine = platform.machine().lower()
|
|
32
|
+
|
|
33
|
+
if system == "darwin":
|
|
34
|
+
os_name = "darwin"
|
|
35
|
+
elif system == "linux":
|
|
36
|
+
os_name = "linux"
|
|
37
|
+
elif system == "windows":
|
|
38
|
+
os_name = "windows"
|
|
39
|
+
else:
|
|
40
|
+
raise RuntimeError(f"Unsupported operating system: {system}")
|
|
41
|
+
|
|
42
|
+
if machine in {"x86_64", "amd64"}:
|
|
43
|
+
arch = "x64"
|
|
44
|
+
elif machine in {"aarch64", "arm64"}:
|
|
45
|
+
arch = "arm64"
|
|
46
|
+
else:
|
|
47
|
+
raise RuntimeError(f"Unsupported CPU architecture: {machine}")
|
|
48
|
+
|
|
49
|
+
return os_name, arch
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def _asset_name(os_name: str, arch: str) -> tuple[str, str]:
|
|
53
|
+
if os_name == "windows":
|
|
54
|
+
return f"ottili-coder-{os_name}-{arch}.zip", "ottili-coder.exe"
|
|
55
|
+
return f"ottili-coder-{os_name}-{arch}.tar.gz", "ottili-coder"
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def _download(url: str, destination: Path) -> None:
|
|
59
|
+
destination.parent.mkdir(parents=True, exist_ok=True)
|
|
60
|
+
request = urllib.request.Request(url, headers={"User-Agent": "ottili-coder-python"})
|
|
61
|
+
with urllib.request.urlopen(request, timeout=120) as response:
|
|
62
|
+
destination.write_bytes(response.read())
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def _extract(archive: Path, destination: Path, binary_name: str) -> Path:
|
|
66
|
+
if archive.suffix == ".zip":
|
|
67
|
+
with zipfile.ZipFile(archive) as zf:
|
|
68
|
+
members = [name for name in zf.namelist() if name.endswith(binary_name)]
|
|
69
|
+
if not members:
|
|
70
|
+
raise RuntimeError(f"Could not find {binary_name} in {archive.name}")
|
|
71
|
+
extracted = destination / binary_name
|
|
72
|
+
extracted.parent.mkdir(parents=True, exist_ok=True)
|
|
73
|
+
with zf.open(members[0]) as src, extracted.open("wb") as dst:
|
|
74
|
+
dst.write(src.read())
|
|
75
|
+
return extracted
|
|
76
|
+
|
|
77
|
+
import tarfile
|
|
78
|
+
|
|
79
|
+
with tarfile.open(archive) as tf:
|
|
80
|
+
members = [member for member in tf.getmembers() if member.name.endswith(binary_name)]
|
|
81
|
+
if not members:
|
|
82
|
+
raise RuntimeError(f"Could not find {binary_name} in {archive.name}")
|
|
83
|
+
tf.extract(members[0], destination, filter="data")
|
|
84
|
+
extracted = destination / members[0].name
|
|
85
|
+
target = destination / binary_name
|
|
86
|
+
if extracted != target:
|
|
87
|
+
if target.exists():
|
|
88
|
+
target.unlink()
|
|
89
|
+
extracted.rename(target)
|
|
90
|
+
return target
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def _install_dir() -> Path:
|
|
94
|
+
override = os.environ.get("OTTILI_CODER_INSTALL_DIR")
|
|
95
|
+
if override:
|
|
96
|
+
return Path(override)
|
|
97
|
+
return Path.home() / ".ottili-coder" / "bin"
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def ensure_binary() -> Path:
|
|
101
|
+
env_path = os.environ.get("OTTILI_CODER_BIN_PATH")
|
|
102
|
+
if env_path:
|
|
103
|
+
path = Path(env_path)
|
|
104
|
+
if path.exists():
|
|
105
|
+
return path
|
|
106
|
+
|
|
107
|
+
install_dir = _install_dir()
|
|
108
|
+
binary_name = "ottili-coder.exe" if platform.system().lower() == "windows" else "ottili-coder"
|
|
109
|
+
installed = install_dir / binary_name
|
|
110
|
+
if installed.exists():
|
|
111
|
+
return installed
|
|
112
|
+
|
|
113
|
+
os_name, arch = _platform_key()
|
|
114
|
+
asset, inner_binary = _asset_name(os_name, arch)
|
|
115
|
+
version = _release_version()
|
|
116
|
+
repo = _release_repo()
|
|
117
|
+
url = f"https://github.com/{repo}/releases/download/v{version}/{asset}"
|
|
118
|
+
|
|
119
|
+
with tempfile.TemporaryDirectory(prefix="ottili-coder-py-") as tmp:
|
|
120
|
+
archive = Path(tmp) / asset
|
|
121
|
+
try:
|
|
122
|
+
_download(url, archive)
|
|
123
|
+
except urllib.error.HTTPError as error:
|
|
124
|
+
raise RuntimeError(
|
|
125
|
+
f"Failed to download Ottili Coder v{version} for {os_name}-{arch} from {url}: {error}"
|
|
126
|
+
) from error
|
|
127
|
+
|
|
128
|
+
extracted = _extract(archive, Path(tmp), inner_binary)
|
|
129
|
+
install_dir.mkdir(parents=True, exist_ok=True)
|
|
130
|
+
if installed.exists():
|
|
131
|
+
installed.unlink()
|
|
132
|
+
shutil.copy2(extracted, installed)
|
|
133
|
+
if os_name != "windows":
|
|
134
|
+
installed.chmod(installed.stat().st_mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH)
|
|
135
|
+
return installed
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def main() -> None:
|
|
139
|
+
binary = ensure_binary()
|
|
140
|
+
completed = subprocess.run([str(binary), *sys.argv[1:]], check=False)
|
|
141
|
+
raise SystemExit(completed.returncode)
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
if __name__ == "__main__":
|
|
145
|
+
main()
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: ottili-coder
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Ottili ONE Coder — autonomous AI coding agent CLI for the terminal
|
|
5
|
+
Project-URL: Homepage, https://ottili.one/coder
|
|
6
|
+
Project-URL: Documentation, https://ottili.one/coder/docs
|
|
7
|
+
Project-URL: Repository, https://github.com/Ottili-ONE/coder-cli
|
|
8
|
+
Project-URL: Issues, https://github.com/Ottili-ONE/coder-cli/issues
|
|
9
|
+
Author-email: Ottili ONE <hello@ottili.one>
|
|
10
|
+
License-Expression: MIT
|
|
11
|
+
Keywords: agent,ai,cli,coder,coding-agent,ottili,ottili-one
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Environment :: Console
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Operating System :: OS Independent
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Topic :: Software Development
|
|
19
|
+
Requires-Python: >=3.9
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
|
|
22
|
+
# ottili-coder (Python)
|
|
23
|
+
|
|
24
|
+
Python distribution for [Ottili ONE Coder](https://ottili.one/coder).
|
|
25
|
+
|
|
26
|
+
Install:
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
pip install ottili-coder
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Run:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
ottili-coder --help
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
The package downloads the platform-native CLI binary on first use and delegates to it.
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
ottili_coder/__init__.py,sha256=6lVkODqYQAb1JnGAnHHB0B1HeJocPr-4yX7qfPn4Z0Q,67
|
|
2
|
+
ottili_coder/cli.py,sha256=pjn0GZTdCrEGi_cHpoMLfUfDgxfDkx-EHZqLWn_dbbM,4637
|
|
3
|
+
ottili_coder-1.0.0.dist-info/METADATA,sha256=sPSMXOpcI3USXGxc-sDQIJ51zR0cRc-9Lc8qE1ebnew,1145
|
|
4
|
+
ottili_coder-1.0.0.dist-info/WHEEL,sha256=mffPy8wBnZQn2VnJUU5jE99KsxaSfiyMHV9Yt0aLVxs,87
|
|
5
|
+
ottili_coder-1.0.0.dist-info/entry_points.txt,sha256=pptViocJK3FZ_7AUcK-_07sHbI12KoY_NtG_W7npQDs,55
|
|
6
|
+
ottili_coder-1.0.0.dist-info/RECORD,,
|