sudoplz 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.
@@ -0,0 +1,48 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+ *.so
6
+ .Python
7
+ build/
8
+ develop-eggs/
9
+ dist/
10
+ downloads/
11
+ eggs/
12
+ .eggs/
13
+ lib/
14
+ lib64/
15
+ parts/
16
+ sdist/
17
+ var/
18
+ wheels/
19
+ *.egg-info/
20
+ .installed.cfg
21
+ *.egg
22
+
23
+ # Virtual environments
24
+ venv/
25
+ env/
26
+ ENV/
27
+ .venv/
28
+
29
+ # Tool caches
30
+ .mypy_cache/
31
+ .ruff_cache/
32
+
33
+ # IDE
34
+ .vscode/
35
+ .idea/
36
+ *.swp
37
+ *.swo
38
+
39
+ # Logs
40
+ *.log
41
+
42
+ # Named pipes (created at runtime)
43
+ /tmp/sudo_command
44
+ /tmp/sudo_response
45
+
46
+ # System
47
+ .DS_Store
48
+ Thumbs.db
@@ -0,0 +1,32 @@
1
+ # sudoplz Project Guidelines
2
+
3
+ ## Project Overview
4
+ sudoplz gives AI coding agents (Claude Code, Cursor, etc.) the ability to run `sudo` commands with case-by-case GUI approval. The sudo password is encrypted with the user's SSH key. The project ships two entry points:
5
+ - `askpass` - Invoked by `sudo -A` via `SUDO_ASKPASS`; performs security checks and prints the decrypted password on approval
6
+ - `sudoplz` - Management CLI (set/get/clear/test/audit, plus TOTP for headless sessions)
7
+
8
+ ## Development Guidelines
9
+
10
+ 1. **Tool Usage**:
11
+ - Use `bash` commands directly for basic operations
12
+ - For complex searches, use the `Task` tool
13
+ - When making multiple bash calls, use `Batch` for parallel execution
14
+
15
+ 2. **Code Style**:
16
+ - Follow existing conventions in the askpass scripts
17
+ - Keep scripts minimal and focused on single responsibilities
18
+ - No unnecessary comments unless explicitly requested
19
+
20
+ 3. **Testing**:
21
+ - Test both `askpass` and `sudoplz` components
22
+ - Verify SSH key encryption/decryption functionality
23
+ - Ensure secure password handling
24
+
25
+ 4. **Security**:
26
+ - Never log or expose passwords in plain text
27
+ - Verify SSH key permissions are properly restricted
28
+ - Follow secure coding practices for password handling
29
+
30
+ 5. **Commits**:
31
+ - Only commit when explicitly requested
32
+ - Follow existing commit style from git history
sudoplz-0.2.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
sudoplz-0.2.0/PKG-INFO ADDED
@@ -0,0 +1,179 @@
1
+ Metadata-Version: 2.4
2
+ Name: sudoplz
3
+ Version: 0.2.0
4
+ Summary: Give AI coding agents case-by-case sudo access via GUI approval, with SSH-key-encrypted password storage
5
+ Project-URL: Homepage, https://github.com/crypdick/sudoplz
6
+ Project-URL: Issues, https://github.com/crypdick/sudoplz/issues
7
+ Project-URL: Source, https://github.com/crypdick/sudoplz
8
+ Author-email: Ricardo Decal <biz@ricardodecal.com>
9
+ License: MIT
10
+ License-File: LICENSE
11
+ Keywords: ai-agents,askpass,claude-code,cursor,security,ssh,sudo
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Environment :: Console
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: Intended Audience :: System Administrators
16
+ Classifier: License :: OSI Approved :: MIT License
17
+ Classifier: Operating System :: MacOS
18
+ Classifier: Operating System :: POSIX :: Linux
19
+ Classifier: Programming Language :: Python :: 3
20
+ Classifier: Programming Language :: Python :: 3.10
21
+ Classifier: Programming Language :: Python :: 3.11
22
+ Classifier: Programming Language :: Python :: 3.12
23
+ Classifier: Programming Language :: Python :: 3.13
24
+ Classifier: Topic :: Security
25
+ Classifier: Topic :: System :: Systems Administration :: Authentication/Directory
26
+ Classifier: Topic :: Utilities
27
+ Requires-Python: >=3.10
28
+ Requires-Dist: keyring>=24
29
+ Description-Content-Type: text/markdown
30
+
31
+ # sudoplz
32
+
33
+ Give Claude Code, Cursor, and other AI coding agents the ability to run `sudo` — with case-by-case GUI approval, no passwordless sudo, no `/etc/sudoers` allowlists.
34
+
35
+ Your sudo password is encrypted with your SSH private key and only decrypted after you approve a dialog showing the exact command about to run. Deny the dialog and nothing happens.
36
+
37
+ ## Why
38
+
39
+ Coding agents can't handle interactive terminal prompts. Ask Claude Code to run `sudo apt install foo` and you get `sudo: Authentication failed`. The common workarounds all have problems:
40
+
41
+ - **Passwordless sudo** gives the agent — and anything else running as your user — unrestricted root.
42
+ - **`/etc/sudoers` allowlists** require predicting every command the agent will ever need. No case-by-case review.
43
+ - **Manual copy-paste** is tedious and breaks the agent's flow.
44
+
45
+ `sudoplz` plugs into `sudo -A`, so the agent runs `sudo -A <command>`, you see a dialog with the exact command, and you click Allow or Deny. Works for any command without pre-declaring what's permitted.
46
+
47
+ This threat model assumes a personal workstation with an encrypted disk and a passphrase-protected SSH key. Not appropriate for shared or production systems.
48
+
49
+ ## Installation
50
+
51
+ 1. **Use traditional `sudo`, not `sudo-rs`.** `sudo-rs` doesn't support askpass. Check with `sudo --version` — it should say "Sudo version 1.x.x". If you're on `sudo-rs`, switch:
52
+ ```bash
53
+ sudo update-alternatives --install /usr/bin/sudo sudo /usr/bin/sudo.ws 100
54
+ sudo update-alternatives --config sudo # pick sudo.ws
55
+ ```
56
+ 2. Make sure you have an SSH key (ed25519, ecdsa, rsa, or dsa).
57
+ 3. Install system dependencies:
58
+ - [`age`](https://github.com/FiloSottile/age) — required if your SSH key is Ed25519 (the most common case today). `sudo pacman -S age` / `sudo apt install age` / `brew install age`.
59
+ - `zenity` on Linux — provides the GUI approval dialog. Pre-installed on most GNOME-based distros; `sudo apt install zenity` if missing. Not needed on macOS (uses AppleScript).
60
+ 4. Install the tools with [`uv`](https://docs.astral.sh/uv/):
61
+ ```bash
62
+ uv tool install .
63
+ ```
64
+ This puts `askpass` and `sudoplz` on your PATH.
65
+ 5. Point `SUDO_ASKPASS` at the installed binary (add to `~/.bashrc`, `~/.zshrc`, etc.):
66
+ ```bash
67
+ export SUDO_ASKPASS="$(which askpass)"
68
+ ```
69
+ 6. Store your sudo password:
70
+ ```bash
71
+ sudoplz set
72
+ ```
73
+
74
+ ## Usage
75
+
76
+ Your agent (or you) runs `sudo -A <command>`. A dialog pops up showing the command. You approve or deny.
77
+
78
+ ```bash
79
+ sudo -A apt install foo
80
+ ```
81
+
82
+ Gotcha: `sudo -n` explicitly disallows prompting and will never trigger askpass. Always use `-A`.
83
+
84
+ Test the integration with:
85
+
86
+ ```bash
87
+ sudoplz test
88
+ ```
89
+
90
+ ## Security
91
+
92
+ ### Encryption at rest
93
+
94
+ Passwords are encrypted with your SSH key:
95
+
96
+ - **Ed25519**: `age` encryption, stored at `~/.sudo_askpass.age`
97
+ - **RSA/ECDSA/DSA**: OpenSSL asymmetric encryption, stored at `~/.sudo_askpass.ssh`
98
+
99
+ Encrypted files have 600 permissions. Key preference: ed25519 > ecdsa > rsa > dsa. Falls back to the system keyring if available. Refuses plain text storage.
100
+
101
+ ### Defense in depth
102
+
103
+ Encryption alone doesn't cover every abuse path — anything running as your user can in principle request decryption. The askpass script runs these checks on every invocation; any failure means no decryption:
104
+
105
+ - **Caller path whitelist.** Only decrypts when the caller's working directory is on an allowlist (home, `/tmp`, etc.). Blocks invocations from unexpected locations like `/var/tmp/malicious`.
106
+ - **Caller process whitelist.** Parent process must be on an allowlist (sudo, your shell, your IDE, your deploy tool). Keeps arbitrary binaries from invoking askpass directly.
107
+ - **User confirmation.** A GUI dialog asks for approval on each decryption, so any sudo elevation you didn't initiate is visible and can be denied.
108
+ - **Rate limiting.** Configurable max-attempts-per-hour and lockout window. Contains runaway scripts and brute-force attempts.
109
+ - **Password expiration.** Stored passwords age out automatically (default: 1 week). A stolen blob becomes useless once it expires, even with your SSH key.
110
+
111
+ Configure these in `~/.config/sudoplz/config.json` — an example is shipped as `askpass-config.json` in the repo; copy it and edit.
112
+
113
+ ### Why age for Ed25519?
114
+
115
+ Ed25519 is a signing algorithm (EdDSA), not encryption. OpenSSL handles RSA encryption directly, but Ed25519 keys can't do asymmetric encryption at all. `age` was designed to work with SSH keys including Ed25519.
116
+
117
+ ### SSH key unlocking
118
+
119
+ If your SSH key has a passphrase (recommended), the askpass tool will:
120
+
121
+ 1. Check whether the key is loaded in ssh-agent
122
+ 2. Prompt for the passphrase via GUI if it isn't
123
+ 3. Load the key into ssh-agent for the session
124
+
125
+ You enter the passphrase once per session. After that, sudo commands only need the confirmation dialog. You need a running ssh-agent — most desktop environments start one on login; if not, `eval "$(ssh-agent -s)"` in your shell startup.
126
+
127
+ This works under `sudo -A` even though sudo strips `SSH_AUTH_SOCK`: the script reconnects to your running ssh-agent.
128
+
129
+ ## Commands
130
+
131
+ ```bash
132
+ sudoplz set # Store password (terminal prompt; expires per config, 1 week default)
133
+ sudoplz set-totp # Store password with TOTP verification (headless)
134
+ sudoplz totp-setup # Set up TOTP for headless sessions
135
+ sudoplz get # Check if password exists
136
+ sudoplz clear # Remove password
137
+ sudoplz test # Test sudo integration
138
+ sudoplz audit # Show recent askpass usage
139
+ ```
140
+
141
+ ## Headless/SSH usage with TOTP
142
+
143
+ For servers or SSH sessions without a display, authenticate with TOTP.
144
+
145
+ ### Initial setup (run once from a GUI session)
146
+
147
+ ```bash
148
+ sudoplz totp-setup
149
+ ```
150
+
151
+ Prints a TOTP secret and an `otpauth://` URL to add to your authenticator app.
152
+
153
+ ### Setting a password from a headless session
154
+
155
+ ```bash
156
+ sudoplz set-totp
157
+ ```
158
+
159
+ Enter your 6-digit TOTP code, then your password.
160
+
161
+ ### Using sudo with TOTP
162
+
163
+ When `DISPLAY` isn't set, askpass prompts for a TOTP code:
164
+
165
+ ```bash
166
+ # Interactive — prompts for TOTP code
167
+ sudo -A command
168
+
169
+ # Non-interactive — pass TOTP via environment
170
+ TOTP="123456" sudo -A command
171
+ ```
172
+
173
+ ## Credits
174
+
175
+ The idea — an SSH-key-encrypted sudo password served via `SUDO_ASKPASS`, gated by a confirmation dialog — is from [GlassOnTin/secure-askpass](https://github.com/GlassOnTin/secure-askpass). That project is dormant; `sudoplz` is a substantially rewritten and cleaned up fork. Thanks to [@GlassOnTin](https://github.com/GlassOnTin) for the original idea.
176
+
177
+ ## License
178
+
179
+ MIT — see LICENSE.
@@ -0,0 +1,149 @@
1
+ # sudoplz
2
+
3
+ Give Claude Code, Cursor, and other AI coding agents the ability to run `sudo` — with case-by-case GUI approval, no passwordless sudo, no `/etc/sudoers` allowlists.
4
+
5
+ Your sudo password is encrypted with your SSH private key and only decrypted after you approve a dialog showing the exact command about to run. Deny the dialog and nothing happens.
6
+
7
+ ## Why
8
+
9
+ Coding agents can't handle interactive terminal prompts. Ask Claude Code to run `sudo apt install foo` and you get `sudo: Authentication failed`. The common workarounds all have problems:
10
+
11
+ - **Passwordless sudo** gives the agent — and anything else running as your user — unrestricted root.
12
+ - **`/etc/sudoers` allowlists** require predicting every command the agent will ever need. No case-by-case review.
13
+ - **Manual copy-paste** is tedious and breaks the agent's flow.
14
+
15
+ `sudoplz` plugs into `sudo -A`, so the agent runs `sudo -A <command>`, you see a dialog with the exact command, and you click Allow or Deny. Works for any command without pre-declaring what's permitted.
16
+
17
+ This threat model assumes a personal workstation with an encrypted disk and a passphrase-protected SSH key. Not appropriate for shared or production systems.
18
+
19
+ ## Installation
20
+
21
+ 1. **Use traditional `sudo`, not `sudo-rs`.** `sudo-rs` doesn't support askpass. Check with `sudo --version` — it should say "Sudo version 1.x.x". If you're on `sudo-rs`, switch:
22
+ ```bash
23
+ sudo update-alternatives --install /usr/bin/sudo sudo /usr/bin/sudo.ws 100
24
+ sudo update-alternatives --config sudo # pick sudo.ws
25
+ ```
26
+ 2. Make sure you have an SSH key (ed25519, ecdsa, rsa, or dsa).
27
+ 3. Install system dependencies:
28
+ - [`age`](https://github.com/FiloSottile/age) — required if your SSH key is Ed25519 (the most common case today). `sudo pacman -S age` / `sudo apt install age` / `brew install age`.
29
+ - `zenity` on Linux — provides the GUI approval dialog. Pre-installed on most GNOME-based distros; `sudo apt install zenity` if missing. Not needed on macOS (uses AppleScript).
30
+ 4. Install the tools with [`uv`](https://docs.astral.sh/uv/):
31
+ ```bash
32
+ uv tool install .
33
+ ```
34
+ This puts `askpass` and `sudoplz` on your PATH.
35
+ 5. Point `SUDO_ASKPASS` at the installed binary (add to `~/.bashrc`, `~/.zshrc`, etc.):
36
+ ```bash
37
+ export SUDO_ASKPASS="$(which askpass)"
38
+ ```
39
+ 6. Store your sudo password:
40
+ ```bash
41
+ sudoplz set
42
+ ```
43
+
44
+ ## Usage
45
+
46
+ Your agent (or you) runs `sudo -A <command>`. A dialog pops up showing the command. You approve or deny.
47
+
48
+ ```bash
49
+ sudo -A apt install foo
50
+ ```
51
+
52
+ Gotcha: `sudo -n` explicitly disallows prompting and will never trigger askpass. Always use `-A`.
53
+
54
+ Test the integration with:
55
+
56
+ ```bash
57
+ sudoplz test
58
+ ```
59
+
60
+ ## Security
61
+
62
+ ### Encryption at rest
63
+
64
+ Passwords are encrypted with your SSH key:
65
+
66
+ - **Ed25519**: `age` encryption, stored at `~/.sudo_askpass.age`
67
+ - **RSA/ECDSA/DSA**: OpenSSL asymmetric encryption, stored at `~/.sudo_askpass.ssh`
68
+
69
+ Encrypted files have 600 permissions. Key preference: ed25519 > ecdsa > rsa > dsa. Falls back to the system keyring if available. Refuses plain text storage.
70
+
71
+ ### Defense in depth
72
+
73
+ Encryption alone doesn't cover every abuse path — anything running as your user can in principle request decryption. The askpass script runs these checks on every invocation; any failure means no decryption:
74
+
75
+ - **Caller path whitelist.** Only decrypts when the caller's working directory is on an allowlist (home, `/tmp`, etc.). Blocks invocations from unexpected locations like `/var/tmp/malicious`.
76
+ - **Caller process whitelist.** Parent process must be on an allowlist (sudo, your shell, your IDE, your deploy tool). Keeps arbitrary binaries from invoking askpass directly.
77
+ - **User confirmation.** A GUI dialog asks for approval on each decryption, so any sudo elevation you didn't initiate is visible and can be denied.
78
+ - **Rate limiting.** Configurable max-attempts-per-hour and lockout window. Contains runaway scripts and brute-force attempts.
79
+ - **Password expiration.** Stored passwords age out automatically (default: 1 week). A stolen blob becomes useless once it expires, even with your SSH key.
80
+
81
+ Configure these in `~/.config/sudoplz/config.json` — an example is shipped as `askpass-config.json` in the repo; copy it and edit.
82
+
83
+ ### Why age for Ed25519?
84
+
85
+ Ed25519 is a signing algorithm (EdDSA), not encryption. OpenSSL handles RSA encryption directly, but Ed25519 keys can't do asymmetric encryption at all. `age` was designed to work with SSH keys including Ed25519.
86
+
87
+ ### SSH key unlocking
88
+
89
+ If your SSH key has a passphrase (recommended), the askpass tool will:
90
+
91
+ 1. Check whether the key is loaded in ssh-agent
92
+ 2. Prompt for the passphrase via GUI if it isn't
93
+ 3. Load the key into ssh-agent for the session
94
+
95
+ You enter the passphrase once per session. After that, sudo commands only need the confirmation dialog. You need a running ssh-agent — most desktop environments start one on login; if not, `eval "$(ssh-agent -s)"` in your shell startup.
96
+
97
+ This works under `sudo -A` even though sudo strips `SSH_AUTH_SOCK`: the script reconnects to your running ssh-agent.
98
+
99
+ ## Commands
100
+
101
+ ```bash
102
+ sudoplz set # Store password (terminal prompt; expires per config, 1 week default)
103
+ sudoplz set-totp # Store password with TOTP verification (headless)
104
+ sudoplz totp-setup # Set up TOTP for headless sessions
105
+ sudoplz get # Check if password exists
106
+ sudoplz clear # Remove password
107
+ sudoplz test # Test sudo integration
108
+ sudoplz audit # Show recent askpass usage
109
+ ```
110
+
111
+ ## Headless/SSH usage with TOTP
112
+
113
+ For servers or SSH sessions without a display, authenticate with TOTP.
114
+
115
+ ### Initial setup (run once from a GUI session)
116
+
117
+ ```bash
118
+ sudoplz totp-setup
119
+ ```
120
+
121
+ Prints a TOTP secret and an `otpauth://` URL to add to your authenticator app.
122
+
123
+ ### Setting a password from a headless session
124
+
125
+ ```bash
126
+ sudoplz set-totp
127
+ ```
128
+
129
+ Enter your 6-digit TOTP code, then your password.
130
+
131
+ ### Using sudo with TOTP
132
+
133
+ When `DISPLAY` isn't set, askpass prompts for a TOTP code:
134
+
135
+ ```bash
136
+ # Interactive — prompts for TOTP code
137
+ sudo -A command
138
+
139
+ # Non-interactive — pass TOTP via environment
140
+ TOTP="123456" sudo -A command
141
+ ```
142
+
143
+ ## Credits
144
+
145
+ The idea — an SSH-key-encrypted sudo password served via `SUDO_ASKPASS`, gated by a confirmation dialog — is from [GlassOnTin/secure-askpass](https://github.com/GlassOnTin/secure-askpass). That project is dormant; `sudoplz` is a substantially rewritten and cleaned up fork. Thanks to [@GlassOnTin](https://github.com/GlassOnTin) for the original idea.
146
+
147
+ ## License
148
+
149
+ MIT — see LICENSE.
@@ -0,0 +1,15 @@
1
+ {
2
+ "require_user_confirmation": true,
3
+ "allowed_paths": [
4
+ "/tmp/",
5
+ "/var/tmp/"
6
+ ],
7
+ "expiration_hours": 168,
8
+ "allowed_processes": [
9
+ "sudo",
10
+ "bash",
11
+ "sh"
12
+ ],
13
+ "max_attempts_per_hour": 120,
14
+ "lockout_minutes": 15
15
+ }
@@ -0,0 +1,66 @@
1
+ [project]
2
+ name = "sudoplz"
3
+ version = "0.2.0"
4
+ description = "Give AI coding agents case-by-case sudo access via GUI approval, with SSH-key-encrypted password storage"
5
+ readme = "README.md"
6
+ license = { text = "MIT" }
7
+ requires-python = ">=3.10"
8
+ authors = [
9
+ { name = "Ricardo Decal", email = "biz@ricardodecal.com" },
10
+ ]
11
+ keywords = ["sudo", "askpass", "ssh", "ai-agents", "claude-code", "cursor", "security"]
12
+ classifiers = [
13
+ "Development Status :: 4 - Beta",
14
+ "Environment :: Console",
15
+ "Intended Audience :: Developers",
16
+ "Intended Audience :: System Administrators",
17
+ "License :: OSI Approved :: MIT License",
18
+ "Operating System :: MacOS",
19
+ "Operating System :: POSIX :: Linux",
20
+ "Programming Language :: Python :: 3",
21
+ "Programming Language :: Python :: 3.10",
22
+ "Programming Language :: Python :: 3.11",
23
+ "Programming Language :: Python :: 3.12",
24
+ "Programming Language :: Python :: 3.13",
25
+ "Topic :: Security",
26
+ "Topic :: System :: Systems Administration :: Authentication/Directory",
27
+ "Topic :: Utilities",
28
+ ]
29
+ dependencies = [
30
+ "keyring>=24",
31
+ ]
32
+
33
+ [project.urls]
34
+ Homepage = "https://github.com/crypdick/sudoplz"
35
+ Issues = "https://github.com/crypdick/sudoplz/issues"
36
+ Source = "https://github.com/crypdick/sudoplz"
37
+
38
+ [project.scripts]
39
+ askpass = "sudoplz.askpass:main"
40
+ sudoplz = "sudoplz.manager:main"
41
+
42
+ [build-system]
43
+ requires = ["hatchling"]
44
+ build-backend = "hatchling.build"
45
+
46
+ [tool.hatch.build.targets.wheel]
47
+ packages = ["src/sudoplz"]
48
+
49
+ [tool.ruff]
50
+ line-length = 100
51
+ target-version = "py310"
52
+ src = ["src"]
53
+
54
+ [tool.ruff.lint]
55
+ select = ["E", "F", "I", "UP", "B", "SIM"]
56
+
57
+ [tool.mypy]
58
+ python_version = "3.10"
59
+ strict = true
60
+ files = ["src"]
61
+
62
+ [dependency-groups]
63
+ dev = [
64
+ "ruff>=0.6",
65
+ "mypy>=1.10",
66
+ ]
@@ -0,0 +1,3 @@
1
+ """Secure SSH-key-encrypted askpass helper for sudo."""
2
+
3
+ __version__ = "0.2.0"