ssh-server-manager 0.2.0__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.
Files changed (34) hide show
  1. ssh_server_manager-0.2.0/PKG-INFO +173 -0
  2. ssh_server_manager-0.2.0/README.md +140 -0
  3. ssh_server_manager-0.2.0/pyproject.toml +58 -0
  4. ssh_server_manager-0.2.0/scripts/ssh_server_manager/__init__.py +4 -0
  5. ssh_server_manager-0.2.0/scripts/ssh_server_manager/askpass.py +63 -0
  6. ssh_server_manager-0.2.0/scripts/ssh_server_manager/assets/ui/app.js +452 -0
  7. ssh_server_manager-0.2.0/scripts/ssh_server_manager/assets/ui/index.html +188 -0
  8. ssh_server_manager-0.2.0/scripts/ssh_server_manager/assets/ui/styles.css +104 -0
  9. ssh_server_manager-0.2.0/scripts/ssh_server_manager/auth.py +189 -0
  10. ssh_server_manager-0.2.0/scripts/ssh_server_manager/cli.py +406 -0
  11. ssh_server_manager-0.2.0/scripts/ssh_server_manager/db.py +477 -0
  12. ssh_server_manager-0.2.0/scripts/ssh_server_manager/importer.py +149 -0
  13. ssh_server_manager-0.2.0/scripts/ssh_server_manager/paths.py +80 -0
  14. ssh_server_manager-0.2.0/scripts/ssh_server_manager/service.py +102 -0
  15. ssh_server_manager-0.2.0/scripts/ssh_server_manager/ssh_config.py +162 -0
  16. ssh_server_manager-0.2.0/scripts/ssh_server_manager/ssh_runner.py +262 -0
  17. ssh_server_manager-0.2.0/scripts/ssh_server_manager/validation.py +89 -0
  18. ssh_server_manager-0.2.0/scripts/ssh_server_manager/vault.py +113 -0
  19. ssh_server_manager-0.2.0/scripts/ssh_server_manager/webapp.py +377 -0
  20. ssh_server_manager-0.2.0/scripts/ssh_server_manager.egg-info/PKG-INFO +173 -0
  21. ssh_server_manager-0.2.0/scripts/ssh_server_manager.egg-info/SOURCES.txt +32 -0
  22. ssh_server_manager-0.2.0/scripts/ssh_server_manager.egg-info/dependency_links.txt +1 -0
  23. ssh_server_manager-0.2.0/scripts/ssh_server_manager.egg-info/entry_points.txt +3 -0
  24. ssh_server_manager-0.2.0/scripts/ssh_server_manager.egg-info/requires.txt +10 -0
  25. ssh_server_manager-0.2.0/scripts/ssh_server_manager.egg-info/top_level.txt +1 -0
  26. ssh_server_manager-0.2.0/setup.cfg +4 -0
  27. ssh_server_manager-0.2.0/tests/test_askpass.py +18 -0
  28. ssh_server_manager-0.2.0/tests/test_auth.py +18 -0
  29. ssh_server_manager-0.2.0/tests/test_db.py +58 -0
  30. ssh_server_manager-0.2.0/tests/test_importer.py +28 -0
  31. ssh_server_manager-0.2.0/tests/test_runner.py +61 -0
  32. ssh_server_manager-0.2.0/tests/test_service.py +28 -0
  33. ssh_server_manager-0.2.0/tests/test_ssh_config.py +43 -0
  34. ssh_server_manager-0.2.0/tests/test_webapp.py +95 -0
@@ -0,0 +1,173 @@
1
+ Metadata-Version: 2.4
2
+ Name: ssh-server-manager
3
+ Version: 0.2.0
4
+ Summary: Local-first SSH host and credential manager for humans and AI agents — OS-keychain vault, loopback web UI, agent-safe by design
5
+ License: MIT
6
+ Project-URL: Homepage, https://xiayh0107.github.io/servers-connect/
7
+ Project-URL: Repository, https://github.com/xiayh0107/servers-connect
8
+ Project-URL: Documentation, https://github.com/xiayh0107/servers-connect/tree/main/ssh-server-manager/docs
9
+ Project-URL: Changelog, https://github.com/xiayh0107/servers-connect/blob/main/ssh-server-manager/CHANGELOG.md
10
+ Keywords: ssh,credentials,keychain,keyring,agent-skill,devops
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Environment :: Console
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Intended Audience :: System Administrators
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Operating System :: MacOS
17
+ Classifier: Operating System :: Microsoft :: Windows
18
+ Classifier: Operating System :: POSIX :: Linux
19
+ Classifier: Programming Language :: Python :: 3
20
+ Classifier: Topic :: System :: Systems Administration
21
+ Classifier: Topic :: Security
22
+ Requires-Python: >=3.10
23
+ Description-Content-Type: text/markdown
24
+ Requires-Dist: argon2-cffi>=23.1
25
+ Requires-Dist: fastapi>=0.115
26
+ Requires-Dist: keyring>=25.0
27
+ Requires-Dist: platformdirs>=4.0
28
+ Requires-Dist: uvicorn>=0.34
29
+ Requires-Dist: webauthn>=2.2
30
+ Provides-Extra: test
31
+ Requires-Dist: httpx>=0.28; extra == "test"
32
+ Requires-Dist: pytest>=8.0; extra == "test"
33
+
34
+ # SSH Server Manager
35
+
36
+ **A local-first SSH host and credential manager for humans and AI agents.**
37
+ One CLI (`serverctl`) and a loopback-only web UI to add, import, test, and
38
+ connect to your servers — with every password and passphrase stored in your
39
+ operating system's native credential vault, never in a plaintext file.
40
+
41
+ [![test](https://github.com/xiayh0107/servers-connect/actions/workflows/test.yml/badge.svg)](https://github.com/xiayh0107/servers-connect/actions/workflows/test.yml)
42
+ [![release](https://img.shields.io/github/v/release/xiayh0107/servers-connect)](https://github.com/xiayh0107/servers-connect/releases)
43
+ [![license](https://img.shields.io/badge/license-MIT-green.svg)](../LICENSE)
44
+
45
+ [简体中文说明](README.zh-CN.md) · [Documentation](docs/) · [Security model](docs/security.md) · [Website](https://xiayh0107.github.io/servers-connect/)
46
+
47
+ > 🤖 **Using an AI agent?** The skill deploys to every agent on your machine
48
+ > in one line — dependencies, links, and health check included. See the
49
+ > [agent deployment guide](docs/ai-agents.md).
50
+
51
+ ---
52
+
53
+ ## Why this exists
54
+
55
+ Most SSH managers make you choose between convenience and custody:
56
+
57
+ - **Cloud-synced clients** (subscription GUIs) keep your credentials on
58
+ someone else's infrastructure.
59
+ - **Plain `~/.ssh/config`** keeps you in control but handles no passwords,
60
+ no vault, and gives AI coding agents an easy way to leak secrets.
61
+ - **`sshpass`-style wrappers** put passwords in files, env vars, or shell
62
+ history.
63
+
64
+ SSH Server Manager is different by construction:
65
+
66
+ | Property | How |
67
+ |---|---|
68
+ | Secrets never touch disk in plaintext | macOS Keychain / Windows Credential Locker / Linux Secret Service via `keyring`; unsafe backends are rejected as a hard error |
69
+ | Your `~/.ssh/config` is never modified | A separate managed config is rendered and included alongside it |
70
+ | The web UI is unreachable from the network | Loopback-only bind, one-time launch token, CSRF + Origin checks, strict CSP |
71
+ | Revealing a stored secret requires re-auth | WebAuthn passkey (Touch ID / Windows Hello) or an Argon2id master password; grants are single-use and expire in 30 s |
72
+ | AI agents can drive it without seeing secrets | Every command has `--json`; SSH authentication happens through AskPass so secrets never appear in argv, env, logs, or model context |
73
+ | Host keys are always verified | `StrictHostKeyChecking` is never weakened, by policy and by code |
74
+
75
+ ## Quick start
76
+
77
+ ```bash
78
+ git clone https://github.com/xiayh0107/servers-connect.git
79
+ cd servers-connect/ssh-server-manager
80
+ ./scripts/bootstrap # Windows: scripts\bootstrap.cmd
81
+ ./scripts/serverctl doctor # verify ssh, vault backend, and dependencies
82
+ ```
83
+
84
+ Add a server and connect:
85
+
86
+ ```bash
87
+ ./scripts/serverctl credential add-password work-password # prompts locally, stores in OS vault
88
+ ./scripts/serverctl server add web1 --hostname web1.example.com --username deploy --credential work-password
89
+ ./scripts/serverctl server test web1
90
+ ./scripts/serverctl connect web1
91
+ ```
92
+
93
+ Or import everything you already have:
94
+
95
+ ```bash
96
+ ./scripts/serverctl server import # preview only
97
+ ./scripts/serverctl server import --apply
98
+ ```
99
+
100
+ Prefer a GUI? `./scripts/serverctl ui` opens the local web interface in your
101
+ browser with a one-time tokenized URL.
102
+
103
+ ## Features
104
+
105
+ - **Connection profiles** — alias, host, port, user, notes, ordered
106
+ ProxyJump chains (with cycle detection).
107
+ - **Three credential kinds** — vault-backed passwords, private keys with
108
+ optional vault-backed passphrases, and ssh-agent/OpenSSH defaults.
109
+ Credentials are reusable across servers and protected against deletion
110
+ while referenced.
111
+ - **OpenSSH import** — parses your `~/.ssh/config` (following `Include`),
112
+ resolves each literal alias with `ssh -G`, previews before applying.
113
+ - **Managed config rendering** — atomic, user-only-permission writes to
114
+ `~/.ssh/ssh-server-manager.conf`; your original config always loads last
115
+ so unrelated defaults keep working.
116
+ - **Connection testing** — `server test` reports latency and a classified
117
+ error code (`authentication-failed`, `host-key-untrusted`, `timeout`,
118
+ `dns-failed`, …) and records history.
119
+ - **Remote execution** — `serverctl exec alias -- cmd args`, with `--shell`
120
+ for pipelines, `--stdin`/`--stdin-binary` for streaming files, `--reuse N`
121
+ for ControlMaster connection sharing (macOS/Linux), and `--json` for
122
+ machine-readable results.
123
+ - **Local web UI** — manage servers and credentials, test connections,
124
+ import config, and reveal a stored secret after passkey or master-password
125
+ re-authentication.
126
+ - **Diagnostics** — `serverctl doctor` checks ssh availability and version,
127
+ vault backend safety, database and config paths, and Python dependencies.
128
+
129
+ ## Platform support
130
+
131
+ | | macOS | Linux | Windows |
132
+ |---|---|---|---|
133
+ | CLI + web UI | ✅ | ✅ | ✅ |
134
+ | Credential vault | Keychain | Secret Service (gnome-keyring / KWallet / KeePassXC) | Credential Locker |
135
+ | Secret reveal re-auth | Touch ID passkey or master password | passkey or master password | Windows Hello passkey or master password |
136
+ | Connection reuse (`--reuse`) | ✅ | ✅ | — (OpenSSH ControlMaster needs Unix sockets; a warning is printed) |
137
+
138
+ Details and headless-server notes: [docs/platforms.md](docs/platforms.md).
139
+ CI runs the full test suite on all three platforms.
140
+
141
+ ## For AI agents
142
+
143
+ This project ships as an Agent Skill ([SKILL.md](SKILL.md)) so Claude Code,
144
+ Codex, and other agents can manage servers safely: agents get structured
145
+ JSON output and connection error classification, while the AskPass
146
+ architecture keeps secrets out of the model's context by design.
147
+
148
+ Deploy the skill with one line:
149
+
150
+ ```bash
151
+ curl -fsSL https://raw.githubusercontent.com/xiayh0107/servers-connect/main/install.sh | sh
152
+ ```
153
+
154
+ It links the skill into every detected agent (`~/.claude/skills`,
155
+ `~/.codex/skills`, …), installs dependencies, and runs `doctor`. Windows
156
+ uses `install.ps1`. See [docs/ai-agents.md](docs/ai-agents.md).
157
+
158
+ ## Documentation
159
+
160
+ | Doc | Contents |
161
+ |---|---|
162
+ | [docs/installation.md](docs/installation.md) | Per-platform install, requirements, bootstrap |
163
+ | [docs/quickstart.md](docs/quickstart.md) | First 10 minutes, common workflows |
164
+ | [docs/cli.md](docs/cli.md) | Complete `serverctl` reference |
165
+ | [docs/web-ui.md](docs/web-ui.md) | Web UI walkthrough and its security gates |
166
+ | [docs/security.md](docs/security.md) | Threat model and security invariants |
167
+ | [docs/platforms.md](docs/platforms.md) | Platform-specific behavior, headless Linux |
168
+ | [docs/ai-agents.md](docs/ai-agents.md) | Using with Claude Code / Codex / MCP |
169
+ | [docs/faq.md](docs/faq.md) | Troubleshooting and common questions |
170
+
171
+ ## License
172
+
173
+ [MIT](../LICENSE)
@@ -0,0 +1,140 @@
1
+ # SSH Server Manager
2
+
3
+ **A local-first SSH host and credential manager for humans and AI agents.**
4
+ One CLI (`serverctl`) and a loopback-only web UI to add, import, test, and
5
+ connect to your servers — with every password and passphrase stored in your
6
+ operating system's native credential vault, never in a plaintext file.
7
+
8
+ [![test](https://github.com/xiayh0107/servers-connect/actions/workflows/test.yml/badge.svg)](https://github.com/xiayh0107/servers-connect/actions/workflows/test.yml)
9
+ [![release](https://img.shields.io/github/v/release/xiayh0107/servers-connect)](https://github.com/xiayh0107/servers-connect/releases)
10
+ [![license](https://img.shields.io/badge/license-MIT-green.svg)](../LICENSE)
11
+
12
+ [简体中文说明](README.zh-CN.md) · [Documentation](docs/) · [Security model](docs/security.md) · [Website](https://xiayh0107.github.io/servers-connect/)
13
+
14
+ > 🤖 **Using an AI agent?** The skill deploys to every agent on your machine
15
+ > in one line — dependencies, links, and health check included. See the
16
+ > [agent deployment guide](docs/ai-agents.md).
17
+
18
+ ---
19
+
20
+ ## Why this exists
21
+
22
+ Most SSH managers make you choose between convenience and custody:
23
+
24
+ - **Cloud-synced clients** (subscription GUIs) keep your credentials on
25
+ someone else's infrastructure.
26
+ - **Plain `~/.ssh/config`** keeps you in control but handles no passwords,
27
+ no vault, and gives AI coding agents an easy way to leak secrets.
28
+ - **`sshpass`-style wrappers** put passwords in files, env vars, or shell
29
+ history.
30
+
31
+ SSH Server Manager is different by construction:
32
+
33
+ | Property | How |
34
+ |---|---|
35
+ | Secrets never touch disk in plaintext | macOS Keychain / Windows Credential Locker / Linux Secret Service via `keyring`; unsafe backends are rejected as a hard error |
36
+ | Your `~/.ssh/config` is never modified | A separate managed config is rendered and included alongside it |
37
+ | The web UI is unreachable from the network | Loopback-only bind, one-time launch token, CSRF + Origin checks, strict CSP |
38
+ | Revealing a stored secret requires re-auth | WebAuthn passkey (Touch ID / Windows Hello) or an Argon2id master password; grants are single-use and expire in 30 s |
39
+ | AI agents can drive it without seeing secrets | Every command has `--json`; SSH authentication happens through AskPass so secrets never appear in argv, env, logs, or model context |
40
+ | Host keys are always verified | `StrictHostKeyChecking` is never weakened, by policy and by code |
41
+
42
+ ## Quick start
43
+
44
+ ```bash
45
+ git clone https://github.com/xiayh0107/servers-connect.git
46
+ cd servers-connect/ssh-server-manager
47
+ ./scripts/bootstrap # Windows: scripts\bootstrap.cmd
48
+ ./scripts/serverctl doctor # verify ssh, vault backend, and dependencies
49
+ ```
50
+
51
+ Add a server and connect:
52
+
53
+ ```bash
54
+ ./scripts/serverctl credential add-password work-password # prompts locally, stores in OS vault
55
+ ./scripts/serverctl server add web1 --hostname web1.example.com --username deploy --credential work-password
56
+ ./scripts/serverctl server test web1
57
+ ./scripts/serverctl connect web1
58
+ ```
59
+
60
+ Or import everything you already have:
61
+
62
+ ```bash
63
+ ./scripts/serverctl server import # preview only
64
+ ./scripts/serverctl server import --apply
65
+ ```
66
+
67
+ Prefer a GUI? `./scripts/serverctl ui` opens the local web interface in your
68
+ browser with a one-time tokenized URL.
69
+
70
+ ## Features
71
+
72
+ - **Connection profiles** — alias, host, port, user, notes, ordered
73
+ ProxyJump chains (with cycle detection).
74
+ - **Three credential kinds** — vault-backed passwords, private keys with
75
+ optional vault-backed passphrases, and ssh-agent/OpenSSH defaults.
76
+ Credentials are reusable across servers and protected against deletion
77
+ while referenced.
78
+ - **OpenSSH import** — parses your `~/.ssh/config` (following `Include`),
79
+ resolves each literal alias with `ssh -G`, previews before applying.
80
+ - **Managed config rendering** — atomic, user-only-permission writes to
81
+ `~/.ssh/ssh-server-manager.conf`; your original config always loads last
82
+ so unrelated defaults keep working.
83
+ - **Connection testing** — `server test` reports latency and a classified
84
+ error code (`authentication-failed`, `host-key-untrusted`, `timeout`,
85
+ `dns-failed`, …) and records history.
86
+ - **Remote execution** — `serverctl exec alias -- cmd args`, with `--shell`
87
+ for pipelines, `--stdin`/`--stdin-binary` for streaming files, `--reuse N`
88
+ for ControlMaster connection sharing (macOS/Linux), and `--json` for
89
+ machine-readable results.
90
+ - **Local web UI** — manage servers and credentials, test connections,
91
+ import config, and reveal a stored secret after passkey or master-password
92
+ re-authentication.
93
+ - **Diagnostics** — `serverctl doctor` checks ssh availability and version,
94
+ vault backend safety, database and config paths, and Python dependencies.
95
+
96
+ ## Platform support
97
+
98
+ | | macOS | Linux | Windows |
99
+ |---|---|---|---|
100
+ | CLI + web UI | ✅ | ✅ | ✅ |
101
+ | Credential vault | Keychain | Secret Service (gnome-keyring / KWallet / KeePassXC) | Credential Locker |
102
+ | Secret reveal re-auth | Touch ID passkey or master password | passkey or master password | Windows Hello passkey or master password |
103
+ | Connection reuse (`--reuse`) | ✅ | ✅ | — (OpenSSH ControlMaster needs Unix sockets; a warning is printed) |
104
+
105
+ Details and headless-server notes: [docs/platforms.md](docs/platforms.md).
106
+ CI runs the full test suite on all three platforms.
107
+
108
+ ## For AI agents
109
+
110
+ This project ships as an Agent Skill ([SKILL.md](SKILL.md)) so Claude Code,
111
+ Codex, and other agents can manage servers safely: agents get structured
112
+ JSON output and connection error classification, while the AskPass
113
+ architecture keeps secrets out of the model's context by design.
114
+
115
+ Deploy the skill with one line:
116
+
117
+ ```bash
118
+ curl -fsSL https://raw.githubusercontent.com/xiayh0107/servers-connect/main/install.sh | sh
119
+ ```
120
+
121
+ It links the skill into every detected agent (`~/.claude/skills`,
122
+ `~/.codex/skills`, …), installs dependencies, and runs `doctor`. Windows
123
+ uses `install.ps1`. See [docs/ai-agents.md](docs/ai-agents.md).
124
+
125
+ ## Documentation
126
+
127
+ | Doc | Contents |
128
+ |---|---|
129
+ | [docs/installation.md](docs/installation.md) | Per-platform install, requirements, bootstrap |
130
+ | [docs/quickstart.md](docs/quickstart.md) | First 10 minutes, common workflows |
131
+ | [docs/cli.md](docs/cli.md) | Complete `serverctl` reference |
132
+ | [docs/web-ui.md](docs/web-ui.md) | Web UI walkthrough and its security gates |
133
+ | [docs/security.md](docs/security.md) | Threat model and security invariants |
134
+ | [docs/platforms.md](docs/platforms.md) | Platform-specific behavior, headless Linux |
135
+ | [docs/ai-agents.md](docs/ai-agents.md) | Using with Claude Code / Codex / MCP |
136
+ | [docs/faq.md](docs/faq.md) | Troubleshooting and common questions |
137
+
138
+ ## License
139
+
140
+ [MIT](../LICENSE)
@@ -0,0 +1,58 @@
1
+ [project]
2
+ name = "ssh-server-manager"
3
+ version = "0.2.0"
4
+ description = "Local-first SSH host and credential manager for humans and AI agents — OS-keychain vault, loopback web UI, agent-safe by design"
5
+ readme = "README.md"
6
+ license = {text = "MIT"}
7
+ requires-python = ">=3.10"
8
+ keywords = ["ssh", "credentials", "keychain", "keyring", "agent-skill", "devops"]
9
+ classifiers = [
10
+ "Development Status :: 4 - Beta",
11
+ "Environment :: Console",
12
+ "Intended Audience :: Developers",
13
+ "Intended Audience :: System Administrators",
14
+ "License :: OSI Approved :: MIT License",
15
+ "Operating System :: MacOS",
16
+ "Operating System :: Microsoft :: Windows",
17
+ "Operating System :: POSIX :: Linux",
18
+ "Programming Language :: Python :: 3",
19
+ "Topic :: System :: Systems Administration",
20
+ "Topic :: Security",
21
+ ]
22
+
23
+ dependencies = [
24
+ "argon2-cffi>=23.1",
25
+ "fastapi>=0.115",
26
+ "keyring>=25.0",
27
+ "platformdirs>=4.0",
28
+ "uvicorn>=0.34",
29
+ "webauthn>=2.2",
30
+ ]
31
+
32
+ [project.urls]
33
+ Homepage = "https://xiayh0107.github.io/servers-connect/"
34
+ Repository = "https://github.com/xiayh0107/servers-connect"
35
+ Documentation = "https://github.com/xiayh0107/servers-connect/tree/main/ssh-server-manager/docs"
36
+ Changelog = "https://github.com/xiayh0107/servers-connect/blob/main/ssh-server-manager/CHANGELOG.md"
37
+
38
+ [build-system]
39
+ requires = ["setuptools>=68"]
40
+ build-backend = "setuptools.build_meta"
41
+
42
+ [project.scripts]
43
+ serverctl = "ssh_server_manager.cli:main"
44
+ ssh-server-manager-askpass = "ssh_server_manager.askpass:main"
45
+
46
+ [tool.setuptools]
47
+ package-dir = {"" = "scripts"}
48
+ packages = ["ssh_server_manager"]
49
+
50
+ [tool.setuptools.package-data]
51
+ ssh_server_manager = ["assets/ui/*"]
52
+
53
+ [project.optional-dependencies]
54
+ test = ["httpx>=0.28", "pytest>=8.0"]
55
+
56
+ [tool.pytest.ini_options]
57
+ testpaths = ["tests"]
58
+ addopts = "-q"
@@ -0,0 +1,4 @@
1
+ """SSH Server Manager implementation."""
2
+
3
+ __version__ = "0.2.0"
4
+
@@ -0,0 +1,63 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ import os
5
+ import sys
6
+ from pathlib import Path
7
+ from typing import Any
8
+
9
+
10
+ def load_descriptors() -> list[dict[str, Any]]:
11
+ try:
12
+ value = json.loads(os.environ.get("SSM_ASKPASS_MAP", "[]"))
13
+ except json.JSONDecodeError:
14
+ return []
15
+ return value if isinstance(value, list) else []
16
+
17
+
18
+ def choose_descriptor(prompt: str, descriptors: list[dict[str, Any]]) -> dict[str, Any] | None:
19
+ lowered = prompt.casefold()
20
+ if "yes/no" in lowered or ("fingerprint" in lowered and "continue connecting" in lowered):
21
+ return None
22
+ passphrases = [item for item in descriptors if item.get("slot") == "passphrase"]
23
+ if "passphrase" in lowered:
24
+ for item in passphrases:
25
+ key_path = str(item.get("key_path", ""))
26
+ if key_path.casefold() in lowered or Path(key_path).name.casefold() in lowered:
27
+ return item
28
+ return passphrases[0] if len(passphrases) == 1 else None
29
+ passwords = [item for item in descriptors if item.get("slot") == "password"]
30
+ if "password" in lowered:
31
+ scored: list[tuple[int, dict[str, Any]]] = []
32
+ for item in passwords:
33
+ score = sum(
34
+ bool(value and str(value).casefold() in lowered)
35
+ for value in (item.get("username"), item.get("hostname"), item.get("alias"))
36
+ )
37
+ scored.append((score, item))
38
+ scored.sort(key=lambda pair: pair[0], reverse=True)
39
+ if scored and (scored[0][0] > 0 or len(scored) == 1):
40
+ return scored[0][1]
41
+ return None
42
+
43
+
44
+ def main() -> int:
45
+ prompt = sys.argv[1] if len(sys.argv) > 1 else ""
46
+ item = choose_descriptor(prompt, load_descriptors())
47
+ if not item:
48
+ return 1
49
+ try:
50
+ from .vault import get_vault
51
+
52
+ secret = get_vault().get_secret(item["credential_id"], item["slot"])
53
+ except Exception:
54
+ return 1
55
+ if not secret:
56
+ return 1
57
+ sys.stdout.write(secret)
58
+ return 0
59
+
60
+
61
+ if __name__ == "__main__":
62
+ raise SystemExit(main())
63
+