basic-password-manager 0.2.2__tar.gz → 0.2.3__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.
- {basic_password_manager-0.2.2 → basic_password_manager-0.2.3}/PKG-INFO +11 -4
- {basic_password_manager-0.2.2 → basic_password_manager-0.2.3}/README.md +9 -2
- {basic_password_manager-0.2.2 → basic_password_manager-0.2.3}/basic_password_manager.egg-info/PKG-INFO +11 -4
- {basic_password_manager-0.2.2 → basic_password_manager-0.2.3}/basic_password_manager.egg-info/SOURCES.txt +1 -0
- {basic_password_manager-0.2.2 → basic_password_manager-0.2.3}/pw_manager/cli.py +23 -1
- basic_password_manager-0.2.3/pw_manager/logo.py +88 -0
- {basic_password_manager-0.2.2 → basic_password_manager-0.2.3}/pw_manager/tui.py +21 -3
- {basic_password_manager-0.2.2 → basic_password_manager-0.2.3}/pyproject.toml +5 -2
- {basic_password_manager-0.2.2 → basic_password_manager-0.2.3}/LICENSE +0 -0
- {basic_password_manager-0.2.2 → basic_password_manager-0.2.3}/basic_password_manager.egg-info/dependency_links.txt +0 -0
- {basic_password_manager-0.2.2 → basic_password_manager-0.2.3}/basic_password_manager.egg-info/entry_points.txt +0 -0
- {basic_password_manager-0.2.2 → basic_password_manager-0.2.3}/basic_password_manager.egg-info/requires.txt +0 -0
- {basic_password_manager-0.2.2 → basic_password_manager-0.2.3}/basic_password_manager.egg-info/top_level.txt +0 -0
- {basic_password_manager-0.2.2 → basic_password_manager-0.2.3}/pw_manager/__init__.py +0 -0
- {basic_password_manager-0.2.2 → basic_password_manager-0.2.3}/pw_manager/crypto.py +0 -0
- {basic_password_manager-0.2.2 → basic_password_manager-0.2.3}/pw_manager/vault.py +0 -0
- {basic_password_manager-0.2.2 → basic_password_manager-0.2.3}/setup.cfg +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: basic-password-manager
|
|
3
|
-
Version: 0.2.
|
|
4
|
-
Summary:
|
|
3
|
+
Version: 0.2.3
|
|
4
|
+
Summary: The Paladin — a local encrypted password manager that lives in your terminal
|
|
5
5
|
License-Expression: MIT
|
|
6
6
|
Project-URL: Repository, https://github.com/Daisentaur/password-manager
|
|
7
7
|
Project-URL: Changelog, https://github.com/Daisentaur/password-manager/blob/main/CHANGELOG.md
|
|
@@ -13,11 +13,17 @@ Requires-Dist: cryptography
|
|
|
13
13
|
Requires-Dist: textual
|
|
14
14
|
Dynamic: license-file
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
<p align="center">
|
|
17
|
+
<img src="assets/logo.png" width="160" alt="The Paladin — a pixel-art knight" />
|
|
18
|
+
</p>
|
|
19
|
+
|
|
20
|
+
<h1 align="center">The Paladin</h1>
|
|
21
|
+
|
|
22
|
+
<p align="center"><em>Your passwords, guarded locally. No browser, no cloud, no mercy.</em></p>
|
|
17
23
|
|
|
18
24
|
A local, encrypted password manager that lives in your terminal. No browser,
|
|
19
25
|
no cloud, no daemon — one encrypted file on disk and a small Python CLI in
|
|
20
|
-
front of it.
|
|
26
|
+
front of it. (Installs as `basic-password-manager`; answers to `pw`.)
|
|
21
27
|
|
|
22
28
|
```
|
|
23
29
|
$ python pw.py get github
|
|
@@ -74,6 +80,7 @@ Or use the subcommands:
|
|
|
74
80
|
| `pw find "dt bank"` | search names, usernames, notes — every word must match, any order |
|
|
75
81
|
| `pw gen -l 32` | just print a random password |
|
|
76
82
|
| `pw import passwords.csv` | import a browser CSV export (see below) |
|
|
83
|
+
| `pw about` | meet the knight |
|
|
77
84
|
|
|
78
85
|
The vault lives at `~/.local/share/pw-manager/vault`. Set the `PW_VAULT`
|
|
79
86
|
environment variable to put it somewhere else.
|
|
@@ -1,8 +1,14 @@
|
|
|
1
|
-
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="assets/logo.png" width="160" alt="The Paladin — a pixel-art knight" />
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
<h1 align="center">The Paladin</h1>
|
|
6
|
+
|
|
7
|
+
<p align="center"><em>Your passwords, guarded locally. No browser, no cloud, no mercy.</em></p>
|
|
2
8
|
|
|
3
9
|
A local, encrypted password manager that lives in your terminal. No browser,
|
|
4
10
|
no cloud, no daemon — one encrypted file on disk and a small Python CLI in
|
|
5
|
-
front of it.
|
|
11
|
+
front of it. (Installs as `basic-password-manager`; answers to `pw`.)
|
|
6
12
|
|
|
7
13
|
```
|
|
8
14
|
$ python pw.py get github
|
|
@@ -59,6 +65,7 @@ Or use the subcommands:
|
|
|
59
65
|
| `pw find "dt bank"` | search names, usernames, notes — every word must match, any order |
|
|
60
66
|
| `pw gen -l 32` | just print a random password |
|
|
61
67
|
| `pw import passwords.csv` | import a browser CSV export (see below) |
|
|
68
|
+
| `pw about` | meet the knight |
|
|
62
69
|
|
|
63
70
|
The vault lives at `~/.local/share/pw-manager/vault`. Set the `PW_VAULT`
|
|
64
71
|
environment variable to put it somewhere else.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: basic-password-manager
|
|
3
|
-
Version: 0.2.
|
|
4
|
-
Summary:
|
|
3
|
+
Version: 0.2.3
|
|
4
|
+
Summary: The Paladin — a local encrypted password manager that lives in your terminal
|
|
5
5
|
License-Expression: MIT
|
|
6
6
|
Project-URL: Repository, https://github.com/Daisentaur/password-manager
|
|
7
7
|
Project-URL: Changelog, https://github.com/Daisentaur/password-manager/blob/main/CHANGELOG.md
|
|
@@ -13,11 +13,17 @@ Requires-Dist: cryptography
|
|
|
13
13
|
Requires-Dist: textual
|
|
14
14
|
Dynamic: license-file
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
<p align="center">
|
|
17
|
+
<img src="assets/logo.png" width="160" alt="The Paladin — a pixel-art knight" />
|
|
18
|
+
</p>
|
|
19
|
+
|
|
20
|
+
<h1 align="center">The Paladin</h1>
|
|
21
|
+
|
|
22
|
+
<p align="center"><em>Your passwords, guarded locally. No browser, no cloud, no mercy.</em></p>
|
|
17
23
|
|
|
18
24
|
A local, encrypted password manager that lives in your terminal. No browser,
|
|
19
25
|
no cloud, no daemon — one encrypted file on disk and a small Python CLI in
|
|
20
|
-
front of it.
|
|
26
|
+
front of it. (Installs as `basic-password-manager`; answers to `pw`.)
|
|
21
27
|
|
|
22
28
|
```
|
|
23
29
|
$ python pw.py get github
|
|
@@ -74,6 +80,7 @@ Or use the subcommands:
|
|
|
74
80
|
| `pw find "dt bank"` | search names, usernames, notes — every word must match, any order |
|
|
75
81
|
| `pw gen -l 32` | just print a random password |
|
|
76
82
|
| `pw import passwords.csv` | import a browser CSV export (see below) |
|
|
83
|
+
| `pw about` | meet the knight |
|
|
77
84
|
|
|
78
85
|
The vault lives at `~/.local/share/pw-manager/vault`. Set the `PW_VAULT`
|
|
79
86
|
environment variable to put it somewhere else.
|
|
@@ -47,7 +47,28 @@ def cmd_init(args):
|
|
|
47
47
|
sys.exit(f"vault already exists at {VAULT_PATH}")
|
|
48
48
|
os.makedirs(os.path.dirname(VAULT_PATH), exist_ok=True)
|
|
49
49
|
vault.save(VAULT_PATH, ask_master(confirm=True), {})
|
|
50
|
-
|
|
50
|
+
if sys.stdout.isatty():
|
|
51
|
+
from . import logo
|
|
52
|
+
|
|
53
|
+
print(logo.says(f"empty vault created at {VAULT_PATH}"))
|
|
54
|
+
else:
|
|
55
|
+
print(f"empty vault created at {VAULT_PATH}")
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def cmd_about(args):
|
|
59
|
+
from importlib.metadata import PackageNotFoundError, version
|
|
60
|
+
|
|
61
|
+
try:
|
|
62
|
+
v = version("basic-password-manager")
|
|
63
|
+
except PackageNotFoundError:
|
|
64
|
+
v = "dev" # running from source without an install
|
|
65
|
+
if sys.stdout.isatty():
|
|
66
|
+
from . import logo
|
|
67
|
+
|
|
68
|
+
print(logo.ansi())
|
|
69
|
+
print(f"The Paladin ♞ v{v}")
|
|
70
|
+
print("your passwords, guarded locally — no browser, no cloud, no mercy")
|
|
71
|
+
print("https://github.com/Daisentaur/password-manager")
|
|
51
72
|
|
|
52
73
|
|
|
53
74
|
def cmd_add(args):
|
|
@@ -198,6 +219,7 @@ def main():
|
|
|
198
219
|
f.add_argument("term")
|
|
199
220
|
i = sub.add_parser("import", help="import a browser CSV export")
|
|
200
221
|
i.add_argument("csv_file")
|
|
222
|
+
sub.add_parser("about", help="meet the knight")
|
|
201
223
|
|
|
202
224
|
args = p.parse_args()
|
|
203
225
|
if args.command is None:
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"""The Paladin, as terminal pixel art.
|
|
2
|
+
|
|
3
|
+
Extracted once from assets/logo.png (each letter below is one pixel; dev-time
|
|
4
|
+
script, no image libraries at runtime). Rendered with half-block characters:
|
|
5
|
+
one character cell shows two pixels — top half foreground, bottom half
|
|
6
|
+
background.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from rich.style import Style
|
|
10
|
+
from rich.text import Text
|
|
11
|
+
|
|
12
|
+
PALETTE = {
|
|
13
|
+
"k": (0, 0, 0), "s": (140, 139, 147), "w": (194, 191, 198),
|
|
14
|
+
"o": (243, 243, 245), "g": (86, 85, 91), "d": (32, 32, 32),
|
|
15
|
+
"l": (158, 157, 163), "b": (246, 120, 59), "r": (143, 64, 47),
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
ROWS = [
|
|
19
|
+
"......kkk.........",
|
|
20
|
+
".....kswwkk.......",
|
|
21
|
+
"....wwwwwwwk......",
|
|
22
|
+
"...kwwwwwwwok.....",
|
|
23
|
+
"...kowwwwwwwk.....",
|
|
24
|
+
"...kgdooodgwk.....",
|
|
25
|
+
"...kdddddddwk...k.",
|
|
26
|
+
"...kwwdddwwwk..kok",
|
|
27
|
+
"..kkwwwddwwwk.kgl.",
|
|
28
|
+
".ookoowddookk.glk.",
|
|
29
|
+
"kgskkkwookkkbblk..",
|
|
30
|
+
"kssswwkkksskkrk...",
|
|
31
|
+
".kkkssgggwwksk....",
|
|
32
|
+
"...ksswwwwwkk.....",
|
|
33
|
+
"...kgkkkkggk......",
|
|
34
|
+
"....k.....k.......",
|
|
35
|
+
]
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def _pixel_pairs():
|
|
39
|
+
"""Yield rows of (top_color, bottom_color) tuples, one per char cell."""
|
|
40
|
+
for top, bottom in zip(ROWS[::2], ROWS[1::2]):
|
|
41
|
+
yield [
|
|
42
|
+
(PALETTE.get(t), PALETTE.get(b))
|
|
43
|
+
for t, b in zip(top, bottom)
|
|
44
|
+
]
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def ansi(indent: str = "") -> str:
|
|
48
|
+
"""The knight as a raw ANSI truecolor string, for print()."""
|
|
49
|
+
out = []
|
|
50
|
+
for row in _pixel_pairs():
|
|
51
|
+
line = indent
|
|
52
|
+
for top, bottom in row:
|
|
53
|
+
if top is None and bottom is None:
|
|
54
|
+
line += "\x1b[0m "
|
|
55
|
+
elif bottom is None:
|
|
56
|
+
line += "\x1b[0m\x1b[38;2;%d;%d;%dm▀" % top
|
|
57
|
+
elif top is None:
|
|
58
|
+
line += "\x1b[0m\x1b[38;2;%d;%d;%dm▄" % bottom
|
|
59
|
+
else:
|
|
60
|
+
line += "\x1b[38;2;%d;%d;%dm\x1b[48;2;%d;%d;%dm▀" % (top + bottom)
|
|
61
|
+
out.append(line + "\x1b[0m")
|
|
62
|
+
return "\n".join(out)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def rich_text() -> Text:
|
|
66
|
+
"""The knight as a Rich Text, for Textual widgets."""
|
|
67
|
+
text = Text()
|
|
68
|
+
for row in _pixel_pairs():
|
|
69
|
+
for top, bottom in row:
|
|
70
|
+
if top is None and bottom is None:
|
|
71
|
+
text.append(" ")
|
|
72
|
+
elif bottom is None:
|
|
73
|
+
text.append("▀", Style(color="rgb(%d,%d,%d)" % top))
|
|
74
|
+
elif top is None:
|
|
75
|
+
text.append("▄", Style(color="rgb(%d,%d,%d)" % bottom))
|
|
76
|
+
else:
|
|
77
|
+
text.append("▀", Style(color="rgb(%d,%d,%d)" % top, bgcolor="rgb(%d,%d,%d)" % bottom))
|
|
78
|
+
text.append("\n")
|
|
79
|
+
return text
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def says(message: str) -> str:
|
|
83
|
+
"""Cowsay, but it's a knight."""
|
|
84
|
+
border = "_" * (len(message) + 2)
|
|
85
|
+
return (
|
|
86
|
+
f" {border}\n< {message} >\n {'-' * (len(message) + 2)}\n"
|
|
87
|
+
f" \\\n" + ansi(indent=" ")
|
|
88
|
+
)
|
|
@@ -17,11 +17,14 @@ from textual.screen import ModalScreen, Screen
|
|
|
17
17
|
from textual.theme import Theme
|
|
18
18
|
from textual.widgets import DataTable, Footer, Input, Label, Static
|
|
19
19
|
|
|
20
|
-
from . import vault
|
|
20
|
+
from . import logo, vault
|
|
21
21
|
from .cli import VAULT_PATH, generate, import_csv, to_clipboard
|
|
22
22
|
|
|
23
23
|
THEME_FILE = os.path.expanduser("~/.local/share/pw-manager/theme")
|
|
24
24
|
|
|
25
|
+
APP_NAME = "The Paladin"
|
|
26
|
+
APP_ICON = "♞"
|
|
27
|
+
|
|
25
28
|
TUXEDO_THEMES = [
|
|
26
29
|
Theme(
|
|
27
30
|
name="muted-slate",
|
|
@@ -73,7 +76,8 @@ class UnlockScreen(Screen):
|
|
|
73
76
|
|
|
74
77
|
def compose(self) -> ComposeResult:
|
|
75
78
|
with Vertical(id="unlock-box"):
|
|
76
|
-
yield
|
|
79
|
+
yield Static(logo.rich_text(), id="unlock-logo")
|
|
80
|
+
yield Label(f"{APP_ICON} {APP_NAME}", id="unlock-title")
|
|
77
81
|
yield Input(password=True, placeholder="master password", id="master")
|
|
78
82
|
yield Static("", id="unlock-error")
|
|
79
83
|
|
|
@@ -212,6 +216,7 @@ class MainScreen(Screen):
|
|
|
212
216
|
]
|
|
213
217
|
|
|
214
218
|
def compose(self) -> ComposeResult:
|
|
219
|
+
yield Static(f" {APP_ICON} {APP_NAME}", id="topbar")
|
|
215
220
|
yield Input(placeholder="/ to search", id="search")
|
|
216
221
|
yield DataTable(id="table", cursor_type="row")
|
|
217
222
|
yield Footer()
|
|
@@ -362,13 +367,19 @@ class VaultCommands(Provider):
|
|
|
362
367
|
|
|
363
368
|
|
|
364
369
|
class PwApp(App):
|
|
365
|
-
TITLE =
|
|
370
|
+
TITLE = APP_NAME
|
|
366
371
|
COMMANDS = App.COMMANDS | {VaultCommands}
|
|
367
372
|
|
|
368
373
|
# Colors come from $theme-variables so every theme applies everywhere;
|
|
369
374
|
# widgets without rules here (table, footer, toasts) use Textual's own
|
|
370
375
|
# theme-aware defaults.
|
|
371
376
|
CSS = """
|
|
377
|
+
#topbar {
|
|
378
|
+
height: 1;
|
|
379
|
+
background: $panel;
|
|
380
|
+
color: $primary;
|
|
381
|
+
text-style: bold;
|
|
382
|
+
}
|
|
372
383
|
#unlock-box {
|
|
373
384
|
align: center middle;
|
|
374
385
|
width: 44;
|
|
@@ -377,7 +388,14 @@ class PwApp(App):
|
|
|
377
388
|
background: $surface;
|
|
378
389
|
border: round $panel;
|
|
379
390
|
}
|
|
391
|
+
#unlock-logo {
|
|
392
|
+
width: 100%;
|
|
393
|
+
text-align: center;
|
|
394
|
+
margin-bottom: 1;
|
|
395
|
+
}
|
|
380
396
|
#unlock-title {
|
|
397
|
+
width: 100%;
|
|
398
|
+
text-align: center;
|
|
381
399
|
color: $primary;
|
|
382
400
|
text-style: bold;
|
|
383
401
|
margin-bottom: 1;
|
|
@@ -4,8 +4,8 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "basic-password-manager"
|
|
7
|
-
version = "0.2.
|
|
8
|
-
description = "
|
|
7
|
+
version = "0.2.3"
|
|
8
|
+
description = "The Paladin — a local encrypted password manager that lives in your terminal"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "MIT"
|
|
11
11
|
requires-python = ">=3.10"
|
|
@@ -15,6 +15,9 @@ dependencies = [
|
|
|
15
15
|
"textual",
|
|
16
16
|
]
|
|
17
17
|
|
|
18
|
+
[tool.setuptools]
|
|
19
|
+
packages = ["pw_manager"]
|
|
20
|
+
|
|
18
21
|
[project.scripts]
|
|
19
22
|
pw = "pw_manager.cli:main"
|
|
20
23
|
|
|
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
|