fix: word-boundary fuzzy alias search, _resolve_alias in remove_server
- "tor" now matches "API TOR contabo..." but NOT "investor" - Whole-word match first, substring fallback second - remove_server() now uses _resolve_alias() for consistency Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
47
tools/ssh.py
47
tools/ssh.py
@@ -250,12 +250,39 @@ def list_servers(full=False):
|
||||
print(f"{alias:<20} {stype:<10} {has_key:<6} {notes}")
|
||||
|
||||
|
||||
def _resolve_alias(alias: str, servers: dict) -> str:
|
||||
"""Resolve alias — exact match, then whole-word search, then substring fallback."""
|
||||
if alias in servers:
|
||||
return alias
|
||||
query = alias.lower()
|
||||
# 1) Whole-word match (e.g. "tor" matches "API TOR contabo" but NOT "investor")
|
||||
import re
|
||||
word_re = re.compile(r'\b' + re.escape(query) + r'\b', re.IGNORECASE)
|
||||
word_matches = [a for a in servers if word_re.search(a)]
|
||||
if len(word_matches) == 1:
|
||||
return word_matches[0]
|
||||
if len(word_matches) > 1:
|
||||
print(f"Ambiguous: '{alias}' matches multiple servers:")
|
||||
for m in word_matches:
|
||||
print(f" - {m}")
|
||||
sys.exit(1)
|
||||
# 2) Substring fallback (e.g. "cont" matches "contabo")
|
||||
sub_matches = [a for a in servers if query in a.lower()]
|
||||
if len(sub_matches) == 1:
|
||||
return sub_matches[0]
|
||||
if len(sub_matches) > 1:
|
||||
print(f"Ambiguous: '{alias}' matches multiple servers:")
|
||||
for m in sub_matches:
|
||||
print(f" - {m}")
|
||||
sys.exit(1)
|
||||
print(f"Unknown: '{alias}'. Available: {', '.join(servers.keys())}")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def server_info(alias: str):
|
||||
"""Show server info safe for AI context — NO ip, user, password, port, totp_secret."""
|
||||
_, servers = load_servers()
|
||||
if alias not in servers:
|
||||
print(f"Unknown: {alias}. Available: {', '.join(servers.keys())}")
|
||||
sys.exit(1)
|
||||
alias = _resolve_alias(alias, servers)
|
||||
s = servers[alias]
|
||||
has_key = "yes" if os.path.exists(SSH_KEY_PATH) else "no"
|
||||
print(f"Alias: {s['alias']}")
|
||||
@@ -318,9 +345,7 @@ def add_server(args):
|
||||
def set_note(alias: str, note: str):
|
||||
"""Update server notes — safe for AI (no credentials exposed)."""
|
||||
data, servers = load_servers()
|
||||
if alias not in servers:
|
||||
print(f"Unknown: {alias}. Available: {', '.join(servers.keys())}")
|
||||
sys.exit(1)
|
||||
alias = _resolve_alias(alias, servers)
|
||||
for s in data["servers"]:
|
||||
if s["alias"] == alias:
|
||||
s["notes"] = note
|
||||
@@ -331,9 +356,7 @@ def set_note(alias: str, note: str):
|
||||
|
||||
def remove_server(alias: str):
|
||||
data, servers = load_servers()
|
||||
if alias not in servers:
|
||||
print(f"ERROR: Unknown '{alias}'")
|
||||
sys.exit(1)
|
||||
alias = _resolve_alias(alias, servers)
|
||||
data["servers"] = [s for s in data["servers"] if s["alias"] != alias]
|
||||
save_servers(data)
|
||||
remove_from_ssh_config(alias)
|
||||
@@ -395,12 +418,10 @@ def main():
|
||||
if cmd == "--remove" and len(sys.argv) >= 3:
|
||||
remove_server(sys.argv[2]); sys.exit(0)
|
||||
|
||||
# Server commands
|
||||
# Server commands — exact match first, then fuzzy search by keyword
|
||||
alias = cmd
|
||||
_, servers = load_servers()
|
||||
if alias not in servers:
|
||||
print(f"Unknown: {alias}. Available: {', '.join(servers.keys())}")
|
||||
sys.exit(1)
|
||||
alias = _resolve_alias(alias, servers)
|
||||
|
||||
server = servers[alias]
|
||||
if len(sys.argv) < 3:
|
||||
|
||||
Reference in New Issue
Block a user