rubber-ducky 1.6.5__tar.gz → 1.6.6__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.
- {rubber_ducky-1.6.5/rubber_ducky.egg-info → rubber_ducky-1.6.6}/PKG-INFO +1 -1
- {rubber_ducky-1.6.5 → rubber_ducky-1.6.6}/ducky/ducky.py +78 -1
- {rubber_ducky-1.6.5 → rubber_ducky-1.6.6}/pyproject.toml +1 -1
- {rubber_ducky-1.6.5 → rubber_ducky-1.6.6/rubber_ducky.egg-info}/PKG-INFO +1 -1
- {rubber_ducky-1.6.5 → rubber_ducky-1.6.6}/LICENSE +0 -0
- {rubber_ducky-1.6.5 → rubber_ducky-1.6.6}/MANIFEST.in +0 -0
- {rubber_ducky-1.6.5 → rubber_ducky-1.6.6}/README.md +0 -0
- {rubber_ducky-1.6.5 → rubber_ducky-1.6.6}/ducky/__init__.py +0 -0
- {rubber_ducky-1.6.5 → rubber_ducky-1.6.6}/ducky/config.py +0 -0
- {rubber_ducky-1.6.5 → rubber_ducky-1.6.6}/ducky/crumb.py +0 -0
- {rubber_ducky-1.6.5 → rubber_ducky-1.6.6}/examples/POLLING_USER_GUIDE.md +0 -0
- {rubber_ducky-1.6.5 → rubber_ducky-1.6.6}/examples/mock-logs/info.txt +0 -0
- {rubber_ducky-1.6.5 → rubber_ducky-1.6.6}/examples/mock-logs/mock-logs.sh +0 -0
- {rubber_ducky-1.6.5 → rubber_ducky-1.6.6}/rubber_ducky.egg-info/SOURCES.txt +0 -0
- {rubber_ducky-1.6.5 → rubber_ducky-1.6.6}/rubber_ducky.egg-info/dependency_links.txt +0 -0
- {rubber_ducky-1.6.5 → rubber_ducky-1.6.6}/rubber_ducky.egg-info/entry_points.txt +0 -0
- {rubber_ducky-1.6.5 → rubber_ducky-1.6.6}/rubber_ducky.egg-info/requires.txt +0 -0
- {rubber_ducky-1.6.5 → rubber_ducky-1.6.6}/rubber_ducky.egg-info/top_level.txt +0 -0
- {rubber_ducky-1.6.5 → rubber_ducky-1.6.6}/setup.cfg +0 -0
|
@@ -14,7 +14,7 @@ from pathlib import Path
|
|
|
14
14
|
from textwrap import dedent
|
|
15
15
|
from typing import Any, Dict, List
|
|
16
16
|
|
|
17
|
-
__version__ = "1.6.
|
|
17
|
+
__version__ = "1.6.5"
|
|
18
18
|
|
|
19
19
|
from .config import ConfigManager
|
|
20
20
|
from .crumb import CrumbManager
|
|
@@ -1170,6 +1170,51 @@ def copy_to_clipboard(text: str) -> bool:
|
|
|
1170
1170
|
return False
|
|
1171
1171
|
|
|
1172
1172
|
|
|
1173
|
+
def check_for_updates() -> None:
|
|
1174
|
+
"""Check PyPI for updates and notify user if a new version is available.
|
|
1175
|
+
|
|
1176
|
+
Checks once per day and caches the result to avoid excessive requests.
|
|
1177
|
+
"""
|
|
1178
|
+
from datetime import datetime, timedelta
|
|
1179
|
+
import urllib.request
|
|
1180
|
+
import json
|
|
1181
|
+
|
|
1182
|
+
cache_file = HISTORY_DIR / "version_check_cache"
|
|
1183
|
+
|
|
1184
|
+
# Check if we've already checked today
|
|
1185
|
+
if cache_file.exists():
|
|
1186
|
+
try:
|
|
1187
|
+
last_check = datetime.fromtimestamp(cache_file.stat().st_mtime)
|
|
1188
|
+
if datetime.now() - last_check < timedelta(days=1):
|
|
1189
|
+
return # Already checked today
|
|
1190
|
+
except Exception:
|
|
1191
|
+
pass
|
|
1192
|
+
|
|
1193
|
+
try:
|
|
1194
|
+
# Query PyPI for latest version
|
|
1195
|
+
req = urllib.request.Request(
|
|
1196
|
+
"https://pypi.org/pypi/rubber-ducky/json",
|
|
1197
|
+
headers={"User-Agent": f"rubber-ducky/{__version__}"}
|
|
1198
|
+
)
|
|
1199
|
+
|
|
1200
|
+
with urllib.request.urlopen(req, timeout=3) as response:
|
|
1201
|
+
data = json.loads(response.read())
|
|
1202
|
+
latest_version = data["info"]["version"]
|
|
1203
|
+
|
|
1204
|
+
if latest_version != __version__:
|
|
1205
|
+
console.print(
|
|
1206
|
+
f"\n[dim]A new version is available: {latest_version} "
|
|
1207
|
+
f"(you have {__version__}). "
|
|
1208
|
+
f"Run: uv tool upgrade rubber-ducky[/dim]\n"
|
|
1209
|
+
)
|
|
1210
|
+
|
|
1211
|
+
# Update cache file timestamp
|
|
1212
|
+
cache_file.touch()
|
|
1213
|
+
except Exception:
|
|
1214
|
+
# Silently fail if no internet or PyPI is down
|
|
1215
|
+
pass
|
|
1216
|
+
|
|
1217
|
+
|
|
1173
1218
|
def confirm(prompt: str, default: bool = False) -> bool:
|
|
1174
1219
|
suffix = " [Y/n]: " if default else " [y/N]: "
|
|
1175
1220
|
try:
|
|
@@ -1219,6 +1264,12 @@ async def ducky() -> None:
|
|
|
1219
1264
|
action="store_true",
|
|
1220
1265
|
help="Suppress startup messages and help text",
|
|
1221
1266
|
)
|
|
1267
|
+
parser.add_argument(
|
|
1268
|
+
"--upgrade",
|
|
1269
|
+
"-u",
|
|
1270
|
+
action="store_true",
|
|
1271
|
+
help="Upgrade rubber-ducky to the latest version using uv",
|
|
1272
|
+
)
|
|
1222
1273
|
parser.add_argument(
|
|
1223
1274
|
"single_prompt",
|
|
1224
1275
|
nargs="*",
|
|
@@ -1227,9 +1278,35 @@ async def ducky() -> None:
|
|
|
1227
1278
|
)
|
|
1228
1279
|
args = parser.parse_args()
|
|
1229
1280
|
|
|
1281
|
+
# Handle upgrade request immediately
|
|
1282
|
+
if args.upgrade:
|
|
1283
|
+
console.print("Upgrading rubber-ducky...", style="yellow")
|
|
1284
|
+
try:
|
|
1285
|
+
result = subprocess.run(
|
|
1286
|
+
["uv", "tool", "upgrade", "rubber-ducky"],
|
|
1287
|
+
capture_output=True,
|
|
1288
|
+
text=True,
|
|
1289
|
+
check=True
|
|
1290
|
+
)
|
|
1291
|
+
console.print(result.stdout, style="dim")
|
|
1292
|
+
console.print("Upgrade complete!", style="green")
|
|
1293
|
+
except subprocess.CalledProcessError as e:
|
|
1294
|
+
console.print(f"Upgrade failed: {e.stderr}", style="red")
|
|
1295
|
+
except FileNotFoundError:
|
|
1296
|
+
console.print("uv not found. Please install uv to use --upgrade.", style="red")
|
|
1297
|
+
return
|
|
1298
|
+
|
|
1230
1299
|
ensure_history_dir()
|
|
1231
1300
|
logger = ConversationLogger(CONVERSATION_LOG_FILE)
|
|
1232
1301
|
|
|
1302
|
+
# Check for updates in background (non-blocking, runs once per day)
|
|
1303
|
+
# Skip if quiet mode, piped input, or single prompt (faster paths)
|
|
1304
|
+
check_piped = not sys.stdin.isatty()
|
|
1305
|
+
if not args.quiet and not args.single_prompt and not check_piped:
|
|
1306
|
+
# Run in a thread to not block startup
|
|
1307
|
+
import threading
|
|
1308
|
+
threading.Thread(target=check_for_updates, daemon=True).start()
|
|
1309
|
+
|
|
1233
1310
|
# Load the last used model from config if no model is specified
|
|
1234
1311
|
config_manager = ConfigManager()
|
|
1235
1312
|
last_model, last_host = config_manager.get_last_model()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|