basic-password-manager 0.2.0__tar.gz → 0.2.2__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.0 → basic_password_manager-0.2.2}/PKG-INFO +12 -4
- {basic_password_manager-0.2.0 → basic_password_manager-0.2.2}/README.md +10 -3
- {basic_password_manager-0.2.0 → basic_password_manager-0.2.2}/basic_password_manager.egg-info/PKG-INFO +12 -4
- {basic_password_manager-0.2.0 → basic_password_manager-0.2.2}/pw_manager/cli.py +16 -11
- {basic_password_manager-0.2.0 → basic_password_manager-0.2.2}/pw_manager/tui.py +132 -8
- {basic_password_manager-0.2.0 → basic_password_manager-0.2.2}/pyproject.toml +2 -1
- {basic_password_manager-0.2.0 → basic_password_manager-0.2.2}/LICENSE +0 -0
- {basic_password_manager-0.2.0 → basic_password_manager-0.2.2}/basic_password_manager.egg-info/SOURCES.txt +0 -0
- {basic_password_manager-0.2.0 → basic_password_manager-0.2.2}/basic_password_manager.egg-info/dependency_links.txt +0 -0
- {basic_password_manager-0.2.0 → basic_password_manager-0.2.2}/basic_password_manager.egg-info/entry_points.txt +0 -0
- {basic_password_manager-0.2.0 → basic_password_manager-0.2.2}/basic_password_manager.egg-info/requires.txt +0 -0
- {basic_password_manager-0.2.0 → basic_password_manager-0.2.2}/basic_password_manager.egg-info/top_level.txt +0 -0
- {basic_password_manager-0.2.0 → basic_password_manager-0.2.2}/pw_manager/__init__.py +0 -0
- {basic_password_manager-0.2.0 → basic_password_manager-0.2.2}/pw_manager/crypto.py +0 -0
- {basic_password_manager-0.2.0 → basic_password_manager-0.2.2}/pw_manager/vault.py +0 -0
- {basic_password_manager-0.2.0 → basic_password_manager-0.2.2}/setup.cfg +0 -0
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: basic-password-manager
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.2
|
|
4
4
|
Summary: 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
|
+
Project-URL: Changelog, https://github.com/Daisentaur/password-manager/blob/main/CHANGELOG.md
|
|
7
8
|
Requires-Python: >=3.10
|
|
8
9
|
Description-Content-Type: text/markdown
|
|
9
10
|
License-File: LICENSE
|
|
@@ -48,6 +49,12 @@ For development: `python3 -m venv venv && ./venv/bin/pip install -e .`
|
|
|
48
49
|
Run `pw` with no arguments for the interactive TUI: unlock, then `/` to
|
|
49
50
|
filter, Enter to copy the selected password, `n` to add, `e` to edit,
|
|
50
51
|
`d` to delete, `t` to switch themes (persisted across runs), `q` to quit.
|
|
52
|
+
The search box matches every word of your query anywhere in an entry —
|
|
53
|
+
name, username, notes, and the password itself — with matches highlighted
|
|
54
|
+
in the theme's accent color (password matches show the row but highlight
|
|
55
|
+
nothing, since passwords are never displayed). Rarer operations live in the
|
|
56
|
+
command palette (`ctrl+p`): importing a browser CSV, changing the master
|
|
57
|
+
password.
|
|
51
58
|
The bundled themes — muted-slate (default), dawn, matrix — are borrowed with
|
|
52
59
|
admiration from [tuxedo](https://github.com/webstonehq/tuxedo); Textual's
|
|
53
60
|
built-in themes are in the picker too.
|
|
@@ -64,7 +71,7 @@ Or use the subcommands:
|
|
|
64
71
|
| `pw passwd` | change the master password |
|
|
65
72
|
| `pw ls` | list entry names |
|
|
66
73
|
| `pw rm github` | delete an entry |
|
|
67
|
-
| `pw find bank` | search
|
|
74
|
+
| `pw find "dt bank"` | search names, usernames, notes — every word must match, any order |
|
|
68
75
|
| `pw gen -l 32` | just print a random password |
|
|
69
76
|
| `pw import passwords.csv` | import a browser CSV export (see below) |
|
|
70
77
|
|
|
@@ -129,9 +136,10 @@ memory for the session; the CLI re-derives the key per command.
|
|
|
129
136
|
|
|
130
137
|
argparse subcommands over the two modules above. Passwords are copied to the
|
|
131
138
|
clipboard (`wl-copy`/`xclip`/`xsel`, whichever exists) rather than printed,
|
|
132
|
-
so they don't sit in your terminal scrollback. `find` searches names,
|
|
139
|
+
so they don't sit in your terminal scrollback. CLI `find` searches names,
|
|
133
140
|
usernames, and notes — deliberately *not* passwords, so fragments of secrets
|
|
134
|
-
never land in your shell history
|
|
141
|
+
never land in your shell history; the TUI search box does cover passwords,
|
|
142
|
+
because nothing typed there is logged anywhere.
|
|
135
143
|
|
|
136
144
|
## Backups
|
|
137
145
|
|
|
@@ -34,6 +34,12 @@ For development: `python3 -m venv venv && ./venv/bin/pip install -e .`
|
|
|
34
34
|
Run `pw` with no arguments for the interactive TUI: unlock, then `/` to
|
|
35
35
|
filter, Enter to copy the selected password, `n` to add, `e` to edit,
|
|
36
36
|
`d` to delete, `t` to switch themes (persisted across runs), `q` to quit.
|
|
37
|
+
The search box matches every word of your query anywhere in an entry —
|
|
38
|
+
name, username, notes, and the password itself — with matches highlighted
|
|
39
|
+
in the theme's accent color (password matches show the row but highlight
|
|
40
|
+
nothing, since passwords are never displayed). Rarer operations live in the
|
|
41
|
+
command palette (`ctrl+p`): importing a browser CSV, changing the master
|
|
42
|
+
password.
|
|
37
43
|
The bundled themes — muted-slate (default), dawn, matrix — are borrowed with
|
|
38
44
|
admiration from [tuxedo](https://github.com/webstonehq/tuxedo); Textual's
|
|
39
45
|
built-in themes are in the picker too.
|
|
@@ -50,7 +56,7 @@ Or use the subcommands:
|
|
|
50
56
|
| `pw passwd` | change the master password |
|
|
51
57
|
| `pw ls` | list entry names |
|
|
52
58
|
| `pw rm github` | delete an entry |
|
|
53
|
-
| `pw find bank` | search
|
|
59
|
+
| `pw find "dt bank"` | search names, usernames, notes — every word must match, any order |
|
|
54
60
|
| `pw gen -l 32` | just print a random password |
|
|
55
61
|
| `pw import passwords.csv` | import a browser CSV export (see below) |
|
|
56
62
|
|
|
@@ -115,9 +121,10 @@ memory for the session; the CLI re-derives the key per command.
|
|
|
115
121
|
|
|
116
122
|
argparse subcommands over the two modules above. Passwords are copied to the
|
|
117
123
|
clipboard (`wl-copy`/`xclip`/`xsel`, whichever exists) rather than printed,
|
|
118
|
-
so they don't sit in your terminal scrollback. `find` searches names,
|
|
124
|
+
so they don't sit in your terminal scrollback. CLI `find` searches names,
|
|
119
125
|
usernames, and notes — deliberately *not* passwords, so fragments of secrets
|
|
120
|
-
never land in your shell history
|
|
126
|
+
never land in your shell history; the TUI search box does cover passwords,
|
|
127
|
+
because nothing typed there is logged anywhere.
|
|
121
128
|
|
|
122
129
|
## Backups
|
|
123
130
|
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: basic-password-manager
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.2
|
|
4
4
|
Summary: 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
|
+
Project-URL: Changelog, https://github.com/Daisentaur/password-manager/blob/main/CHANGELOG.md
|
|
7
8
|
Requires-Python: >=3.10
|
|
8
9
|
Description-Content-Type: text/markdown
|
|
9
10
|
License-File: LICENSE
|
|
@@ -48,6 +49,12 @@ For development: `python3 -m venv venv && ./venv/bin/pip install -e .`
|
|
|
48
49
|
Run `pw` with no arguments for the interactive TUI: unlock, then `/` to
|
|
49
50
|
filter, Enter to copy the selected password, `n` to add, `e` to edit,
|
|
50
51
|
`d` to delete, `t` to switch themes (persisted across runs), `q` to quit.
|
|
52
|
+
The search box matches every word of your query anywhere in an entry —
|
|
53
|
+
name, username, notes, and the password itself — with matches highlighted
|
|
54
|
+
in the theme's accent color (password matches show the row but highlight
|
|
55
|
+
nothing, since passwords are never displayed). Rarer operations live in the
|
|
56
|
+
command palette (`ctrl+p`): importing a browser CSV, changing the master
|
|
57
|
+
password.
|
|
51
58
|
The bundled themes — muted-slate (default), dawn, matrix — are borrowed with
|
|
52
59
|
admiration from [tuxedo](https://github.com/webstonehq/tuxedo); Textual's
|
|
53
60
|
built-in themes are in the picker too.
|
|
@@ -64,7 +71,7 @@ Or use the subcommands:
|
|
|
64
71
|
| `pw passwd` | change the master password |
|
|
65
72
|
| `pw ls` | list entry names |
|
|
66
73
|
| `pw rm github` | delete an entry |
|
|
67
|
-
| `pw find bank` | search
|
|
74
|
+
| `pw find "dt bank"` | search names, usernames, notes — every word must match, any order |
|
|
68
75
|
| `pw gen -l 32` | just print a random password |
|
|
69
76
|
| `pw import passwords.csv` | import a browser CSV export (see below) |
|
|
70
77
|
|
|
@@ -129,9 +136,10 @@ memory for the session; the CLI re-derives the key per command.
|
|
|
129
136
|
|
|
130
137
|
argparse subcommands over the two modules above. Passwords are copied to the
|
|
131
138
|
clipboard (`wl-copy`/`xclip`/`xsel`, whichever exists) rather than printed,
|
|
132
|
-
so they don't sit in your terminal scrollback. `find` searches names,
|
|
139
|
+
so they don't sit in your terminal scrollback. CLI `find` searches names,
|
|
133
140
|
usernames, and notes — deliberately *not* passwords, so fragments of secrets
|
|
134
|
-
never land in your shell history
|
|
141
|
+
never land in your shell history; the TUI search box does cover passwords,
|
|
142
|
+
because nothing typed there is logged anywhere.
|
|
135
143
|
|
|
136
144
|
## Backups
|
|
137
145
|
|
|
@@ -135,16 +135,15 @@ def cmd_gen(args):
|
|
|
135
135
|
|
|
136
136
|
|
|
137
137
|
def cmd_find(args):
|
|
138
|
-
"""
|
|
139
|
-
passwords: a password fragment typed as
|
|
138
|
+
"""Every word of the query must appear somewhere in name/username/notes.
|
|
139
|
+
Deliberately not over passwords: a password fragment typed as a CLI
|
|
140
|
+
argument lands in shell history (the TUI search does cover passwords)."""
|
|
140
141
|
entries = vault.load(VAULT_PATH, ask_master())
|
|
141
|
-
|
|
142
|
+
words = args.term.lower().split()
|
|
142
143
|
hits = {
|
|
143
144
|
name: e
|
|
144
145
|
for name, e in sorted(entries.items())
|
|
145
|
-
if
|
|
146
|
-
or term in e["user"].lower()
|
|
147
|
-
or term in e.get("notes", "").lower()
|
|
146
|
+
if all(w in f"{name} {e['user']} {e.get('notes', '')}".lower() for w in words)
|
|
148
147
|
}
|
|
149
148
|
for name, e in hits.items():
|
|
150
149
|
print(f"{name} ({e['user']})")
|
|
@@ -152,12 +151,11 @@ def cmd_find(args):
|
|
|
152
151
|
sys.exit(f"nothing matches '{args.term}'")
|
|
153
152
|
|
|
154
153
|
|
|
155
|
-
def
|
|
156
|
-
"""
|
|
157
|
-
|
|
158
|
-
entries = vault.load(VAULT_PATH, master)
|
|
154
|
+
def import_csv(csv_file: str, entries: dict) -> int:
|
|
155
|
+
"""Merge a browser CSV export (Chrome: name,url,username,password /
|
|
156
|
+
Firefox: url,...) into entries. Returns how many were added."""
|
|
159
157
|
added = 0
|
|
160
|
-
with open(
|
|
158
|
+
with open(csv_file, newline="") as f:
|
|
161
159
|
for row in csv.DictReader(f):
|
|
162
160
|
name = row.get("name") or urlparse(row.get("url", "")).netloc
|
|
163
161
|
pw = row.get("password")
|
|
@@ -165,6 +163,13 @@ def cmd_import(args):
|
|
|
165
163
|
continue
|
|
166
164
|
entries[name] = {"user": row.get("username", ""), "pw": pw, "notes": ""}
|
|
167
165
|
added += 1
|
|
166
|
+
return added
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def cmd_import(args):
|
|
170
|
+
master = ask_master()
|
|
171
|
+
entries = vault.load(VAULT_PATH, master)
|
|
172
|
+
added = import_csv(args.csv_file, entries)
|
|
168
173
|
vault.save(VAULT_PATH, master, entries)
|
|
169
174
|
print(f"imported {added} entries — now delete {args.csv_file} securely")
|
|
170
175
|
|
|
@@ -5,17 +5,20 @@ src/theme.rs); "muted-slate" is the default. `t` opens the theme picker,
|
|
|
5
5
|
and Textual's own built-in themes are available there too.
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
|
+
import csv
|
|
8
9
|
import os
|
|
9
10
|
|
|
11
|
+
from rich.text import Text
|
|
10
12
|
from textual.app import App, ComposeResult
|
|
11
13
|
from textual.binding import Binding
|
|
14
|
+
from textual.command import DiscoveryHit, Hit, Hits, Provider
|
|
12
15
|
from textual.containers import Vertical
|
|
13
16
|
from textual.screen import ModalScreen, Screen
|
|
14
17
|
from textual.theme import Theme
|
|
15
18
|
from textual.widgets import DataTable, Footer, Input, Label, Static
|
|
16
19
|
|
|
17
20
|
from . import vault
|
|
18
|
-
from .cli import VAULT_PATH, generate, to_clipboard
|
|
21
|
+
from .cli import VAULT_PATH, generate, import_csv, to_clipboard
|
|
19
22
|
|
|
20
23
|
THEME_FILE = os.path.expanduser("~/.local/share/pw-manager/theme")
|
|
21
24
|
|
|
@@ -125,6 +128,53 @@ class EntryModal(ModalScreen):
|
|
|
125
128
|
)
|
|
126
129
|
|
|
127
130
|
|
|
131
|
+
class ImportModal(ModalScreen):
|
|
132
|
+
"""Ask for the path of a browser CSV export."""
|
|
133
|
+
|
|
134
|
+
BINDINGS = [Binding("escape", "dismiss", "cancel")]
|
|
135
|
+
|
|
136
|
+
def compose(self) -> ComposeResult:
|
|
137
|
+
with Vertical(classes="modal-box"):
|
|
138
|
+
yield Label("import browser CSV", classes="modal-title")
|
|
139
|
+
yield Input(placeholder="path to export, e.g. ~/Downloads/passwords.csv", id="i-path")
|
|
140
|
+
yield Static("enter imports · esc cancels", classes="modal-hint")
|
|
141
|
+
|
|
142
|
+
def on_input_submitted(self, event: Input.Submitted) -> None:
|
|
143
|
+
if event.value.strip():
|
|
144
|
+
self.dismiss(os.path.expanduser(event.value.strip()))
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
class PasswdModal(ModalScreen):
|
|
148
|
+
"""Change the master password. Asks for the current one again so a
|
|
149
|
+
walked-up-to unlocked session can't lock the real owner out."""
|
|
150
|
+
|
|
151
|
+
BINDINGS = [Binding("escape", "dismiss", "cancel")]
|
|
152
|
+
|
|
153
|
+
def compose(self) -> ComposeResult:
|
|
154
|
+
with Vertical(classes="modal-box"):
|
|
155
|
+
yield Label("change master password", classes="modal-title")
|
|
156
|
+
yield Input(password=True, placeholder="current master password", id="p-current")
|
|
157
|
+
yield Input(password=True, placeholder="new master password (8+ chars)", id="p-new")
|
|
158
|
+
yield Input(password=True, placeholder="confirm new master password", id="p-confirm")
|
|
159
|
+
yield Static("enter saves · esc cancels", classes="modal-hint")
|
|
160
|
+
yield Static("", id="p-error")
|
|
161
|
+
|
|
162
|
+
def on_input_submitted(self, event: Input.Submitted) -> None:
|
|
163
|
+
current = self.query_one("#p-current", Input).value
|
|
164
|
+
new = self.query_one("#p-new", Input).value
|
|
165
|
+
error = self.query_one("#p-error", Static)
|
|
166
|
+
if current != self.app.master:
|
|
167
|
+
error.update("current master password is wrong")
|
|
168
|
+
return
|
|
169
|
+
if len(new) < 8:
|
|
170
|
+
error.update("new master password must be at least 8 characters")
|
|
171
|
+
return
|
|
172
|
+
if new != self.query_one("#p-confirm", Input).value:
|
|
173
|
+
error.update("new passwords do not match")
|
|
174
|
+
return
|
|
175
|
+
self.dismiss(new)
|
|
176
|
+
|
|
177
|
+
|
|
128
178
|
class ConfirmModal(ModalScreen):
|
|
129
179
|
"""y/n confirmation."""
|
|
130
180
|
|
|
@@ -171,18 +221,40 @@ class MainScreen(Screen):
|
|
|
171
221
|
table.add_columns("name", "username", "notes")
|
|
172
222
|
self.refresh_table()
|
|
173
223
|
table.focus()
|
|
224
|
+
# match-highlight color comes from the theme, so repaint on switch
|
|
225
|
+
self.app.theme_changed_signal.subscribe(self, self._on_theme_change)
|
|
226
|
+
|
|
227
|
+
def _on_theme_change(self, _theme: Theme) -> None:
|
|
228
|
+
self.refresh_table()
|
|
229
|
+
|
|
230
|
+
@staticmethod
|
|
231
|
+
def _highlight(value: str, words: list[str], style: str) -> Text:
|
|
232
|
+
text = Text(value)
|
|
233
|
+
low = value.lower()
|
|
234
|
+
for w in words:
|
|
235
|
+
start = 0
|
|
236
|
+
while (i := low.find(w, start)) != -1:
|
|
237
|
+
text.stylize(style, i, i + len(w))
|
|
238
|
+
start = i + 1
|
|
239
|
+
return text
|
|
174
240
|
|
|
175
241
|
def refresh_table(self) -> None:
|
|
242
|
+
"""Every search word must appear somewhere in the entry — name,
|
|
243
|
+
username, notes, or the password itself. Passwords are searched but
|
|
244
|
+
never displayed, so matches there can't be highlighted (by design)."""
|
|
176
245
|
table = self.query_one(DataTable)
|
|
177
246
|
table.clear()
|
|
178
|
-
|
|
247
|
+
words = self.query_one("#search", Input).value.lower().split()
|
|
248
|
+
style = f"bold {self.app.current_theme.primary}"
|
|
179
249
|
for name, e in sorted(self.app.entries.items()):
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
250
|
+
hay = f"{name} {e['user']} {e.get('notes', '')} {e['pw']}".lower()
|
|
251
|
+
if all(w in hay for w in words):
|
|
252
|
+
table.add_row(
|
|
253
|
+
self._highlight(name, words, style),
|
|
254
|
+
self._highlight(e["user"], words, style),
|
|
255
|
+
self._highlight(e.get("notes", ""), words, style),
|
|
256
|
+
key=name,
|
|
257
|
+
)
|
|
186
258
|
|
|
187
259
|
def save(self) -> None:
|
|
188
260
|
vault.save(VAULT_PATH, self.app.master, self.app.entries)
|
|
@@ -263,8 +335,35 @@ class MainScreen(Screen):
|
|
|
263
335
|
self.query_one(DataTable).focus()
|
|
264
336
|
|
|
265
337
|
|
|
338
|
+
class VaultCommands(Provider):
|
|
339
|
+
"""Palette commands for the unlocked vault. Unlike Textual's built-in
|
|
340
|
+
provider, the query matches descriptions too, not just titles."""
|
|
341
|
+
|
|
342
|
+
@property
|
|
343
|
+
def _commands(self):
|
|
344
|
+
if not isinstance(self.screen, MainScreen):
|
|
345
|
+
return []
|
|
346
|
+
app = self.app
|
|
347
|
+
return [
|
|
348
|
+
("Import browser CSV", "add every entry from a Chrome/Firefox password export", app._import_csv),
|
|
349
|
+
("Change master password", "re-encrypt the vault under a new master password", app._change_master),
|
|
350
|
+
]
|
|
351
|
+
|
|
352
|
+
async def search(self, query: str) -> Hits:
|
|
353
|
+
matcher = self.matcher(query)
|
|
354
|
+
for title, help_text, callback in self._commands:
|
|
355
|
+
score = matcher.match(f"{title} {help_text}")
|
|
356
|
+
if score > 0:
|
|
357
|
+
yield Hit(score, matcher.highlight(title), callback, help=help_text)
|
|
358
|
+
|
|
359
|
+
async def discover(self) -> Hits:
|
|
360
|
+
for title, help_text, callback in self._commands:
|
|
361
|
+
yield DiscoveryHit(title, callback, help=help_text)
|
|
362
|
+
|
|
363
|
+
|
|
266
364
|
class PwApp(App):
|
|
267
365
|
TITLE = "pw-manager"
|
|
366
|
+
COMMANDS = App.COMMANDS | {VaultCommands}
|
|
268
367
|
|
|
269
368
|
# Colors come from $theme-variables so every theme applies everywhere;
|
|
270
369
|
# widgets without rules here (table, footer, toasts) use Textual's own
|
|
@@ -340,6 +439,31 @@ class PwApp(App):
|
|
|
340
439
|
with open(THEME_FILE, "w") as f:
|
|
341
440
|
f.write(theme.name)
|
|
342
441
|
|
|
442
|
+
def _import_csv(self) -> None:
|
|
443
|
+
def done(path: str | None) -> None:
|
|
444
|
+
if not path:
|
|
445
|
+
return
|
|
446
|
+
try:
|
|
447
|
+
added = import_csv(path, self.entries)
|
|
448
|
+
except (OSError, csv.Error) as e:
|
|
449
|
+
self.notify(str(e), severity="error")
|
|
450
|
+
return
|
|
451
|
+
vault.save(VAULT_PATH, self.master, self.entries)
|
|
452
|
+
if isinstance(self.screen, MainScreen):
|
|
453
|
+
self.screen.refresh_table()
|
|
454
|
+
self.notify(f"imported {added} entries — delete the CSV securely")
|
|
455
|
+
|
|
456
|
+
self.push_screen(ImportModal(), done)
|
|
457
|
+
|
|
458
|
+
def _change_master(self) -> None:
|
|
459
|
+
def done(new: str | None) -> None:
|
|
460
|
+
if new:
|
|
461
|
+
self.master = new
|
|
462
|
+
vault.save(VAULT_PATH, self.master, self.entries)
|
|
463
|
+
self.notify("master password changed (old one is now useless)")
|
|
464
|
+
|
|
465
|
+
self.push_screen(PasswdModal(), done)
|
|
466
|
+
|
|
343
467
|
|
|
344
468
|
def main() -> None:
|
|
345
469
|
PwApp().run()
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "basic-password-manager"
|
|
7
|
-
version = "0.2.
|
|
7
|
+
version = "0.2.2"
|
|
8
8
|
description = "Local encrypted password manager that lives in your terminal"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "MIT"
|
|
@@ -20,3 +20,4 @@ pw = "pw_manager.cli:main"
|
|
|
20
20
|
|
|
21
21
|
[project.urls]
|
|
22
22
|
Repository = "https://github.com/Daisentaur/password-manager"
|
|
23
|
+
Changelog = "https://github.com/Daisentaur/password-manager/blob/main/CHANGELOG.md"
|
|
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
|
|
File without changes
|