mirror of
https://github.com/basicmachines-co/basic-memory
synced 2026-06-21 13:47:35 +00:00
chore(deps): update deps and harden security (#825)
Signed-off-by: phernandez <paul@basicmachines.co>
This commit is contained in:
@@ -4,7 +4,7 @@ import os
|
||||
import platform
|
||||
import shutil
|
||||
import subprocess
|
||||
from typing import Optional
|
||||
from typing import Any, Optional, cast
|
||||
|
||||
from rich.console import Console
|
||||
|
||||
@@ -53,7 +53,9 @@ def run_command(command: list[str], check: bool = True) -> subprocess.CompletedP
|
||||
|
||||
|
||||
def install_rclone_macos() -> None:
|
||||
"""Install rclone on macOS using Homebrew or official script."""
|
||||
"""Install rclone on macOS using package managers."""
|
||||
install_errors: list[str] = []
|
||||
|
||||
# Try Homebrew first
|
||||
if shutil.which("brew"):
|
||||
try:
|
||||
@@ -61,35 +63,37 @@ def install_rclone_macos() -> None:
|
||||
run_command(["brew", "install", "rclone"])
|
||||
console.print("[green]rclone installed via Homebrew[/green]")
|
||||
return
|
||||
except RcloneInstallError:
|
||||
console.print(
|
||||
"[yellow]Homebrew installation failed, trying official script...[/yellow]"
|
||||
)
|
||||
except RcloneInstallError as exc:
|
||||
install_errors.append(f"Homebrew failed: {exc}")
|
||||
console.print("[yellow]Homebrew installation failed, trying MacPorts...[/yellow]")
|
||||
|
||||
# Fallback to official script
|
||||
console.print("[blue]Installing rclone via official script...[/blue]")
|
||||
try:
|
||||
run_command(["sh", "-c", "curl https://rclone.org/install.sh | sudo bash"])
|
||||
console.print("[green]rclone installed via official script[/green]")
|
||||
except RcloneInstallError:
|
||||
raise RcloneInstallError(
|
||||
"Failed to install rclone. Please install manually: brew install rclone"
|
||||
)
|
||||
if shutil.which("port"):
|
||||
try:
|
||||
console.print("[blue]Installing rclone via MacPorts...[/blue]")
|
||||
run_command(["sudo", "port", "install", "rclone"])
|
||||
console.print("[green]rclone installed via MacPorts[/green]")
|
||||
return
|
||||
except RcloneInstallError as exc:
|
||||
install_errors.append(f"MacPorts failed: {exc}")
|
||||
console.print("[yellow]MacPorts installation failed[/yellow]")
|
||||
|
||||
details = "\n".join(f"- {error}" for error in install_errors)
|
||||
if details:
|
||||
details = f"\n\nAttempts:\n{details}"
|
||||
raise RcloneInstallError(
|
||||
"Could not install rclone automatically with an available package manager.\n\n"
|
||||
"Install rclone manually with one of:\n"
|
||||
" brew install rclone\n"
|
||||
" sudo port install rclone\n"
|
||||
" Download from https://rclone.org/downloads/ and add rclone to PATH"
|
||||
f"{details}"
|
||||
)
|
||||
|
||||
|
||||
def install_rclone_linux() -> None:
|
||||
"""Install rclone on Linux using package managers or official script."""
|
||||
# Try snap first (most universal)
|
||||
if shutil.which("snap"):
|
||||
try:
|
||||
console.print("[blue]Installing rclone via snap...[/blue]")
|
||||
run_command(["sudo", "snap", "install", "rclone"])
|
||||
console.print("[green]rclone installed via snap[/green]")
|
||||
return
|
||||
except RcloneInstallError:
|
||||
console.print("[yellow]Snap installation failed, trying apt...[/yellow]")
|
||||
"""Install rclone on Linux using package managers."""
|
||||
install_errors: list[str] = []
|
||||
|
||||
# Try apt (Debian/Ubuntu)
|
||||
if shutil.which("apt"):
|
||||
try:
|
||||
console.print("[blue]Installing rclone via apt...[/blue]")
|
||||
@@ -97,18 +101,75 @@ def install_rclone_linux() -> None:
|
||||
run_command(["sudo", "apt", "install", "-y", "rclone"])
|
||||
console.print("[green]rclone installed via apt[/green]")
|
||||
return
|
||||
except RcloneInstallError:
|
||||
console.print("[yellow]apt installation failed, trying official script...[/yellow]")
|
||||
except RcloneInstallError as exc:
|
||||
install_errors.append(f"apt failed: {exc}")
|
||||
console.print("[yellow]apt installation failed, trying dnf...[/yellow]")
|
||||
|
||||
# Fallback to official script
|
||||
console.print("[blue]Installing rclone via official script...[/blue]")
|
||||
try:
|
||||
run_command(["sh", "-c", "curl https://rclone.org/install.sh | sudo bash"])
|
||||
console.print("[green]rclone installed via official script[/green]")
|
||||
except RcloneInstallError:
|
||||
raise RcloneInstallError(
|
||||
"Failed to install rclone. Please install manually: sudo snap install rclone"
|
||||
)
|
||||
if shutil.which("dnf"):
|
||||
try:
|
||||
console.print("[blue]Installing rclone via dnf...[/blue]")
|
||||
run_command(["sudo", "dnf", "install", "-y", "rclone"])
|
||||
console.print("[green]rclone installed via dnf[/green]")
|
||||
return
|
||||
except RcloneInstallError as exc:
|
||||
install_errors.append(f"dnf failed: {exc}")
|
||||
console.print("[yellow]dnf installation failed, trying yum...[/yellow]")
|
||||
|
||||
if shutil.which("yum"):
|
||||
try:
|
||||
console.print("[blue]Installing rclone via yum...[/blue]")
|
||||
run_command(["sudo", "yum", "install", "-y", "rclone"])
|
||||
console.print("[green]rclone installed via yum[/green]")
|
||||
return
|
||||
except RcloneInstallError as exc:
|
||||
install_errors.append(f"yum failed: {exc}")
|
||||
console.print("[yellow]yum installation failed, trying pacman...[/yellow]")
|
||||
|
||||
if shutil.which("pacman"):
|
||||
try:
|
||||
console.print("[blue]Installing rclone via pacman...[/blue]")
|
||||
run_command(["sudo", "pacman", "-S", "--noconfirm", "rclone"])
|
||||
console.print("[green]rclone installed via pacman[/green]")
|
||||
return
|
||||
except RcloneInstallError as exc:
|
||||
install_errors.append(f"pacman failed: {exc}")
|
||||
console.print("[yellow]pacman installation failed, trying zypper...[/yellow]")
|
||||
|
||||
if shutil.which("zypper"):
|
||||
try:
|
||||
console.print("[blue]Installing rclone via zypper...[/blue]")
|
||||
run_command(["sudo", "zypper", "--non-interactive", "install", "rclone"])
|
||||
console.print("[green]rclone installed via zypper[/green]")
|
||||
return
|
||||
except RcloneInstallError as exc:
|
||||
install_errors.append(f"zypper failed: {exc}")
|
||||
console.print("[yellow]zypper installation failed, trying snap...[/yellow]")
|
||||
|
||||
if shutil.which("snap"):
|
||||
try:
|
||||
console.print("[blue]Installing rclone via snap...[/blue]")
|
||||
run_command(["sudo", "snap", "install", "rclone"])
|
||||
console.print("[green]rclone installed via snap[/green]")
|
||||
return
|
||||
except RcloneInstallError as exc:
|
||||
install_errors.append(f"snap failed: {exc}")
|
||||
console.print("[yellow]snap installation failed[/yellow]")
|
||||
|
||||
details = "\n".join(f"- {error}" for error in install_errors)
|
||||
if details:
|
||||
details = f"\n\nAttempts:\n{details}"
|
||||
raise RcloneInstallError(
|
||||
"Could not install rclone automatically with an available package manager.\n\n"
|
||||
"Install rclone manually with one of your OS package managers, for example:\n"
|
||||
" sudo apt install rclone\n"
|
||||
" sudo dnf install rclone\n"
|
||||
" sudo yum install rclone\n"
|
||||
" sudo pacman -S rclone\n"
|
||||
" sudo zypper install rclone\n"
|
||||
" sudo snap install rclone\n"
|
||||
"Or download from https://rclone.org/downloads/ and add rclone to PATH"
|
||||
f"{details}"
|
||||
)
|
||||
|
||||
|
||||
def install_rclone_windows() -> None:
|
||||
@@ -209,27 +270,38 @@ def refresh_windows_path() -> None:
|
||||
if platform.system().lower() != "windows":
|
||||
return
|
||||
|
||||
# Importing here after performing platform detection. Also note that we have to ignore pylance/pyright
|
||||
# warnings about winreg attributes so that "errors" don't appear on non-Windows platforms.
|
||||
# Importing here after performing platform detection. Non-Windows type checkers may still
|
||||
# resolve a stub without registry members, so keep this platform-only module dynamic here.
|
||||
import winreg
|
||||
|
||||
winreg_module = cast(Any, winreg)
|
||||
user_key_path = r"Environment"
|
||||
system_key_path = r"System\CurrentControlSet\Control\Session Manager\Environment"
|
||||
new_path = ""
|
||||
|
||||
# Read user PATH
|
||||
try:
|
||||
reg_key = winreg.OpenKey(winreg.HKEY_CURRENT_USER, user_key_path, 0, winreg.KEY_READ) # type: ignore[reportAttributeAccessIssue]
|
||||
user_path, _ = winreg.QueryValueEx(reg_key, "PATH") # type: ignore[reportAttributeAccessIssue]
|
||||
winreg.CloseKey(reg_key) # type: ignore[reportAttributeAccessIssue]
|
||||
reg_key = winreg_module.OpenKey(
|
||||
winreg_module.HKEY_CURRENT_USER,
|
||||
user_key_path,
|
||||
0,
|
||||
winreg_module.KEY_READ,
|
||||
)
|
||||
user_path, _ = winreg_module.QueryValueEx(reg_key, "PATH")
|
||||
winreg_module.CloseKey(reg_key)
|
||||
except Exception:
|
||||
user_path = ""
|
||||
|
||||
# Read system PATH
|
||||
try:
|
||||
reg_key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, system_key_path, 0, winreg.KEY_READ) # type: ignore[reportAttributeAccessIssue]
|
||||
system_path, _ = winreg.QueryValueEx(reg_key, "PATH") # type: ignore[reportAttributeAccessIssue]
|
||||
winreg.CloseKey(reg_key) # type: ignore[reportAttributeAccessIssue]
|
||||
reg_key = winreg_module.OpenKey(
|
||||
winreg_module.HKEY_LOCAL_MACHINE,
|
||||
system_key_path,
|
||||
0,
|
||||
winreg_module.KEY_READ,
|
||||
)
|
||||
system_path, _ = winreg_module.QueryValueEx(reg_key, "PATH")
|
||||
winreg_module.CloseKey(reg_key)
|
||||
except Exception:
|
||||
system_path = ""
|
||||
|
||||
|
||||
@@ -100,9 +100,7 @@ def set_default_workspace(
|
||||
raise typer.Exit(1)
|
||||
|
||||
if len(matches) > 1:
|
||||
console.print(
|
||||
f"[red]Error: Workspace '{identifier}' matches multiple workspaces.[/red]"
|
||||
)
|
||||
console.print(f"[red]Error: Workspace '{identifier}' matches multiple workspaces.[/red]")
|
||||
console.print(
|
||||
"[dim]Choose one of these matching workspaces by slug:\n"
|
||||
f"{format_workspace_selection_choices(matches)}[/dim]"
|
||||
|
||||
@@ -300,9 +300,7 @@ def _resolve_workspace_id(config, workspace: str | None) -> str | None:
|
||||
console.print(f"[dim]Available:\n{format_workspace_choices(workspaces)}[/dim]")
|
||||
raise typer.Exit(1)
|
||||
if len(matches) > 1:
|
||||
console.print(
|
||||
f"[red]Error: Workspace '{workspace}' matches multiple workspaces.[/red]"
|
||||
)
|
||||
console.print(f"[red]Error: Workspace '{workspace}' matches multiple workspaces.[/red]")
|
||||
console.print(
|
||||
"[dim]Choose one of these matching workspaces by slug:\n"
|
||||
f"{format_workspace_selection_choices(matches)}[/dim]"
|
||||
@@ -575,9 +573,7 @@ def list_projects(
|
||||
_, permalink = row_key
|
||||
project_name = row_names_by_key[row_key]
|
||||
is_attached_row = attached_row_by_permalink.get(permalink) == row_key
|
||||
local_project = (
|
||||
local_projects_by_permalink.get(permalink) if is_attached_row else None
|
||||
)
|
||||
local_project = local_projects_by_permalink.get(permalink) if is_attached_row else None
|
||||
cloud_project = cloud_projects_by_key.get(row_key)
|
||||
cloud_workspace = cloud_workspaces_by_key.get(row_key)
|
||||
configured_name = configured_names_by_permalink.get(permalink)
|
||||
@@ -621,9 +617,7 @@ def list_projects(
|
||||
)
|
||||
is_default = bool(is_attached_row and permalink == default_permalink)
|
||||
|
||||
sync_supported = (
|
||||
cloud_workspace is None or cloud_workspace.workspace_type == "personal"
|
||||
)
|
||||
sync_supported = cloud_workspace is None or cloud_workspace.workspace_type == "personal"
|
||||
has_sync = bool(is_attached_row and entry and entry.local_sync_path and sync_supported)
|
||||
# Determine MCP transport based on project routing mode
|
||||
if entry and entry.mode == ProjectMode.CLOUD:
|
||||
|
||||
Reference in New Issue
Block a user