agentsecrets-cli 3.2.2__tar.gz → 3.2.3__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.
- {agentsecrets_cli-3.2.2 → agentsecrets_cli-3.2.3}/PKG-INFO +1 -1
- {agentsecrets_cli-3.2.2 → agentsecrets_cli-3.2.3}/pyproject.toml +1 -1
- {agentsecrets_cli-3.2.2 → agentsecrets_cli-3.2.3}/src/agentsecrets/binary.py +84 -43
- {agentsecrets_cli-3.2.2 → agentsecrets_cli-3.2.3}/.gitignore +0 -0
- {agentsecrets_cli-3.2.2 → agentsecrets_cli-3.2.3}/README.md +0 -0
- {agentsecrets_cli-3.2.2 → agentsecrets_cli-3.2.3}/src/agentsecrets/__init__.py +0 -0
- {agentsecrets_cli-3.2.2 → agentsecrets_cli-3.2.3}/src/agentsecrets/main.py +0 -0
|
@@ -1,36 +1,97 @@
|
|
|
1
1
|
import os
|
|
2
2
|
import platform
|
|
3
3
|
import sys
|
|
4
|
+
import time
|
|
4
5
|
import urllib.request
|
|
5
6
|
import tarfile
|
|
6
7
|
import zipfile
|
|
7
8
|
import tempfile
|
|
8
9
|
import shutil
|
|
9
10
|
import stat
|
|
10
|
-
|
|
11
11
|
import json
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
"""Fetches the latest version tag from GitHub API."""
|
|
15
|
-
try:
|
|
16
|
-
url = "https://api.github.com/repos/The-17/agentsecrets/releases/latest"
|
|
17
|
-
req = urllib.request.Request(url, headers={'User-Agent': 'agentsecrets-pypi'})
|
|
18
|
-
with urllib.request.urlopen(req) as response:
|
|
19
|
-
data = json.loads(response.read().decode())
|
|
20
|
-
return data['tag_name'].lstrip('v')
|
|
21
|
-
except Exception:
|
|
22
|
-
return None
|
|
13
|
+
GITHUB_REPO = "The-17/agentsecrets"
|
|
23
14
|
|
|
24
15
|
def _get_version():
|
|
25
16
|
try:
|
|
26
|
-
from importlib.metadata import version
|
|
17
|
+
from importlib.metadata import version
|
|
27
18
|
return version("agentsecrets-cli")
|
|
28
|
-
except
|
|
29
|
-
|
|
30
|
-
|
|
19
|
+
except Exception:
|
|
20
|
+
pass
|
|
21
|
+
try:
|
|
22
|
+
import re
|
|
23
|
+
pyproject = os.path.join(os.path.dirname(__file__), "..", "..", "pyproject.toml")
|
|
24
|
+
if os.path.exists(pyproject):
|
|
25
|
+
with open(pyproject, "r", encoding="utf-8") as f:
|
|
26
|
+
m = re.search(r'version\s*=\s*["\']([^"\']+)["\']', f.read())
|
|
27
|
+
if m:
|
|
28
|
+
return m.group(1)
|
|
29
|
+
except Exception:
|
|
30
|
+
pass
|
|
31
|
+
return "dev"
|
|
31
32
|
|
|
32
33
|
VERSION = _get_version()
|
|
33
|
-
|
|
34
|
+
|
|
35
|
+
def compare_versions(v1, v2):
|
|
36
|
+
p1 = [int(x) for x in (v1 or "").lstrip("v").split(".") if x.isdigit()]
|
|
37
|
+
p2 = [int(x) for x in (v2 or "").lstrip("v").split(".") if x.isdigit()]
|
|
38
|
+
while len(p1) < 3: p1.append(0)
|
|
39
|
+
while len(p2) < 3: p2.append(0)
|
|
40
|
+
for i in range(3):
|
|
41
|
+
if p1[i] < p2[i]: return -1
|
|
42
|
+
if p1[i] > p2[i]: return 1
|
|
43
|
+
return 0
|
|
44
|
+
|
|
45
|
+
def check_update_notice():
|
|
46
|
+
try:
|
|
47
|
+
cache_file = os.path.expanduser("~/.agentsecrets/pypi_update_cache.json")
|
|
48
|
+
cache = None
|
|
49
|
+
if os.path.exists(cache_file):
|
|
50
|
+
try:
|
|
51
|
+
with open(cache_file, "r") as f:
|
|
52
|
+
cache = json.load(f)
|
|
53
|
+
except Exception:
|
|
54
|
+
pass
|
|
55
|
+
|
|
56
|
+
now = int(time.time())
|
|
57
|
+
if cache and cache.get("latest_version") and compare_versions(cache["latest_version"], VERSION) > 0:
|
|
58
|
+
sys.stderr.write(
|
|
59
|
+
f"\n\033[33mUpdate available:\033[0m {VERSION} -> \033[32m{cache['latest_version']}\033[0m\n"
|
|
60
|
+
f"Run: \033[36mpip install --upgrade agentsecrets-cli\033[0m to update\n\n"
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
if not cache or not cache.get("last_checked") or now - cache["last_checked"] > 86400:
|
|
64
|
+
try:
|
|
65
|
+
req = urllib.request.Request(
|
|
66
|
+
f"https://api.github.com/repos/{GITHUB_REPO}/releases/latest",
|
|
67
|
+
headers={"User-Agent": "agentsecrets-pypi"}
|
|
68
|
+
)
|
|
69
|
+
with urllib.request.urlopen(req, timeout=2.0) as response:
|
|
70
|
+
data = json.loads(response.read().decode())
|
|
71
|
+
latest = data.get("tag_name", "").lstrip("v")
|
|
72
|
+
if latest:
|
|
73
|
+
os.makedirs(os.path.dirname(cache_file), exist_ok=True)
|
|
74
|
+
with open(cache_file, "w") as f:
|
|
75
|
+
json.dump({"last_checked": now, "latest_version": latest}, f)
|
|
76
|
+
except Exception:
|
|
77
|
+
pass
|
|
78
|
+
except Exception:
|
|
79
|
+
pass
|
|
80
|
+
|
|
81
|
+
def cleanup_old_binaries(base_dir, current_binary_path):
|
|
82
|
+
try:
|
|
83
|
+
if not os.path.exists(base_dir):
|
|
84
|
+
return
|
|
85
|
+
prefix = "agentsecrets.exe_" if platform.system().lower() == "windows" else "agentsecrets_"
|
|
86
|
+
for fname in os.listdir(base_dir):
|
|
87
|
+
full_path = os.path.join(base_dir, fname)
|
|
88
|
+
if fname.startswith(prefix) and full_path != current_binary_path:
|
|
89
|
+
try:
|
|
90
|
+
os.remove(full_path)
|
|
91
|
+
except Exception:
|
|
92
|
+
pass
|
|
93
|
+
except Exception:
|
|
94
|
+
pass
|
|
34
95
|
|
|
35
96
|
def get_platform_info():
|
|
36
97
|
"""Returns a tuple of (os_name, arch_name) compatible with GoReleaser naming."""
|
|
@@ -61,11 +122,9 @@ def ensure_binary():
|
|
|
61
122
|
If not, download it from GitHub Releases.
|
|
62
123
|
Returns the absolute path to the binary.
|
|
63
124
|
"""
|
|
125
|
+
check_update_notice()
|
|
64
126
|
os_name, arch_name = get_platform_info()
|
|
65
127
|
|
|
66
|
-
# Store binaries in a hidden directory in the user's home or package dir
|
|
67
|
-
# For PyPI, it's easier to store in the package directory itself if we have permissions,
|
|
68
|
-
# or ~/.agentsecrets/bin
|
|
69
128
|
base_dir = os.path.expanduser("~/.agentsecrets/bin")
|
|
70
129
|
os.makedirs(base_dir, exist_ok=True)
|
|
71
130
|
|
|
@@ -73,22 +132,14 @@ def ensure_binary():
|
|
|
73
132
|
if os_name == "windows":
|
|
74
133
|
binary_name += ".exe"
|
|
75
134
|
|
|
76
|
-
# Versioned binary path
|
|
77
135
|
binary_path = os.path.join(base_dir, f"{binary_name}_{VERSION}")
|
|
78
136
|
|
|
79
137
|
if os.path.exists(binary_path):
|
|
80
138
|
return binary_path
|
|
81
139
|
|
|
82
|
-
|
|
83
|
-
print(f"AgentSecrets binary not found. Downloading version {VERSION} for {os_name}/{arch_name}...", file=sys.stderr)
|
|
140
|
+
sys.stderr.write(f"AgentSecrets binary not found. Downloading version {VERSION} for {os_name}/{arch_name}...\n")
|
|
84
141
|
|
|
85
142
|
ext = "tar.gz" if os_name != "windows" else "zip"
|
|
86
|
-
# Match the naming template in .goreleaser.yaml: {{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}
|
|
87
|
-
# Note: GoReleaser naming is case-sensitive and uses specific strings.
|
|
88
|
-
# Looking at .goreleaser.yaml: name_template: "{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}"
|
|
89
|
-
# GoReleaser Os: linux, darwin, windows
|
|
90
|
-
# GoReleaser Arch: amd64, arm64
|
|
91
|
-
|
|
92
143
|
asset_name = f"agentsecrets_{VERSION}_{os_name}_{arch_name}.{ext}"
|
|
93
144
|
url = f"https://github.com/{GITHUB_REPO}/releases/download/v{VERSION}/{asset_name}"
|
|
94
145
|
|
|
@@ -96,11 +147,9 @@ def ensure_binary():
|
|
|
96
147
|
with tempfile.TemporaryDirectory() as tmpdir:
|
|
97
148
|
archive_path = os.path.join(tmpdir, asset_name)
|
|
98
149
|
|
|
99
|
-
# Download the archive
|
|
100
150
|
with urllib.request.urlopen(url) as response, open(archive_path, 'wb') as out_file:
|
|
101
151
|
shutil.copyfileobj(response, out_file)
|
|
102
152
|
|
|
103
|
-
# Extract
|
|
104
153
|
if ext == "tar.gz":
|
|
105
154
|
with tarfile.open(archive_path, "r:gz") as tar:
|
|
106
155
|
tar.extractall(path=tmpdir)
|
|
@@ -108,12 +157,8 @@ def ensure_binary():
|
|
|
108
157
|
with zipfile.ZipFile(archive_path, 'r') as zip_ref:
|
|
109
158
|
zip_ref.extractall(tmpdir)
|
|
110
159
|
|
|
111
|
-
# Move the binary to the final location
|
|
112
|
-
# The archive contains the binary in the root
|
|
113
160
|
extracted_binary = os.path.join(tmpdir, binary_name)
|
|
114
161
|
if not os.path.exists(extracted_binary):
|
|
115
|
-
# Maybe it's inside a folder? goreleaser usually puts it in root by default unless configured otherwise
|
|
116
|
-
# Let's check all files in tmpdir
|
|
117
162
|
for root, dirs, files in os.walk(tmpdir):
|
|
118
163
|
if binary_name in files:
|
|
119
164
|
extracted_binary = os.path.join(root, binary_name)
|
|
@@ -121,12 +166,10 @@ def ensure_binary():
|
|
|
121
166
|
|
|
122
167
|
shutil.move(extracted_binary, binary_path)
|
|
123
168
|
|
|
124
|
-
# Ensure executable
|
|
125
169
|
st = os.stat(binary_path)
|
|
126
170
|
os.chmod(binary_path, st.st_mode | stat.S_IEXEC | stat.S_IXGRP | stat.S_IXOTH)
|
|
127
171
|
|
|
128
|
-
# Pre-register with keychain-auth if available
|
|
129
|
-
# the one-time setup spinner on the user's first command.
|
|
172
|
+
# Pre-register with keychain-auth if available
|
|
130
173
|
import subprocess
|
|
131
174
|
kc = shutil.which("keychain-auth")
|
|
132
175
|
if kc:
|
|
@@ -136,21 +179,19 @@ def ensure_binary():
|
|
|
136
179
|
timeout=5, capture_output=True,
|
|
137
180
|
)
|
|
138
181
|
except Exception:
|
|
139
|
-
pass
|
|
182
|
+
pass
|
|
140
183
|
|
|
184
|
+
cleanup_old_binaries(base_dir, binary_path)
|
|
141
185
|
return binary_path
|
|
142
186
|
|
|
143
187
|
except Exception as e:
|
|
144
|
-
# Fallback to checking if a real binary is already in the PATH
|
|
145
188
|
system_binary = shutil.which("agentsecrets")
|
|
146
|
-
# Ensure we don't pick up this Python wrapper itself (infinite loop)
|
|
147
189
|
if system_binary and not system_binary.endswith(".py") and "site-packages" not in system_binary:
|
|
148
190
|
return system_binary
|
|
149
191
|
|
|
150
192
|
raise Exception(
|
|
151
193
|
f"Failed to download AgentSecrets binary from {url}.\n"
|
|
152
194
|
f"Error: {e}\n\n"
|
|
153
|
-
"TIP:
|
|
154
|
-
"
|
|
155
|
-
"This will trigger the build that makes this binary available."
|
|
195
|
+
"TIP: Please verify your internet connection or install directly via Homebrew:\n"
|
|
196
|
+
" brew install The-17/tap/agentsecrets"
|
|
156
197
|
)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|