Compare commits
No commits in common. "main" and "first-release" have entirely different histories.
main
...
first-rele
14 changed files with 8 additions and 364 deletions
|
|
@ -1,29 +0,0 @@
|
||||||
# Contributing to pm 🦀
|
|
||||||
|
|
||||||
First, thanks for your interest in contributing to "pm"! Contributions are what make open-source so amazing.
|
|
||||||
|
|
||||||
## How Can I Contribute?
|
|
||||||
|
|
||||||
### Reporting Bugs
|
|
||||||
|
|
||||||
- Check the Issues tab to make sure the bug hasn't already been reported
|
|
||||||
- If it has not, open an issue with the label `bug` and describe the problem, your operating system, and steps to reproduce the bug.
|
|
||||||
|
|
||||||
### Suggesting Features
|
|
||||||
|
|
||||||
- Open an issue with the label `enhancement`.
|
|
||||||
- Describe the feature you'd like to see and why it would be useful. (e.g., "Add a cli to improve user experience").
|
|
||||||
|
|
||||||
### Pull Requests (Code Contributions)
|
|
||||||
|
|
||||||
- If you've fixed a bug, added a new feature, or even just fixed a README typo, you can submit a pull request with your changes.
|
|
||||||
- Make sure the code is easy to read and understand, include tests for new features or bug fixes and they all pass, and follow the Rust Style Guide (You can use the `cargo fmt` command to make sure it fits the style guide.)
|
|
||||||
- Also since this is a Rust project you will need the standard toolchain to run cargo commands like `cargo build`.
|
|
||||||
|
|
||||||
## Security Note
|
|
||||||
|
|
||||||
If you find a security vulnerability, please do not open a public issue and instead contact me directly so it can be fixed without being disclosed.
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
By contributing, you agree that your contributions will be licensed under the project's MIT License.
|
|
||||||
|
|
@ -2,7 +2,6 @@
|
||||||
name = "pm"
|
name = "pm"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
license = "MIT"
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
argon2 = { version = "0.5.3", features = ["std", "password-hash"] }
|
argon2 = { version = "0.5.3", features = ["std", "password-hash"] }
|
||||||
|
|
|
||||||
21
LICENSE
21
LICENSE
|
|
@ -1,21 +0,0 @@
|
||||||
MIT License
|
|
||||||
|
|
||||||
Copyright (c) 2026 ManOfGoldForever
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
||||||
112
README.md
112
README.md
|
|
@ -1,112 +0,0 @@
|
||||||
# pm 🦀
|
|
||||||
|
|
||||||
A fast, secure, and lightweight password manager CLI built entirely in Rust. No cloud, no tracking, just local encryption.
|
|
||||||
|
|
||||||
## Table of Contents
|
|
||||||
|
|
||||||
- [Why use this?](#why-use-this?)
|
|
||||||
- [Installation](#installation)
|
|
||||||
- [Build from Source](#build-from-source)
|
|
||||||
- [Uninstallation](#uninstallation)
|
|
||||||
- [Usage](#usage)
|
|
||||||
- [Security and Data](#security-and-data)
|
|
||||||
- [Future Plans](#future-plans)
|
|
||||||
- [Contributing](#contributing)
|
|
||||||
- [License](#license)
|
|
||||||
|
|
||||||
### Why use this?
|
|
||||||
|
|
||||||
- **Privacy First:** Your passwords never leave your machine.
|
|
||||||
- **Modern Encryption:** Uses XChaCha20-Poly1305 and Argon2id.
|
|
||||||
- **Memory Safe:** Sensitive data is wiped from RAM immediately after use.
|
|
||||||
- **Lightweight:** Tiny binary with an instant startup and no gui bloat.
|
|
||||||
|
|
||||||
### Installation
|
|
||||||
|
|
||||||
1. Download the latest compressed folder for your OS (.tar.gz for linux/Mac or .zip for Windows) from the [releases](https://github.com/ManOfGoldForever/Rust-Password-Manager/releases/tag/first-release) tab.
|
|
||||||
2. Extract the folder
|
|
||||||
3. Run the installer (this also works when updating to a newer downloaded version):
|
|
||||||
|
|
||||||
- **Linux/Mac:** Open a terminal in the extracted folder and run: `chmod +x linux-mac-install.sh && ./linux-mac-install.sh`
|
|
||||||
- **Windows:** Right-click windows-install.ps1 and select `Run with PowerShell`
|
|
||||||
|
|
||||||
This script moves the pm binary to your "PATH" so you can use it anywhere by typing pm in a terminal.
|
|
||||||
|
|
||||||
### Build from Source
|
|
||||||
|
|
||||||
If you would rather compile the binary yourself rather than using pre-built releases, follow these steps:
|
|
||||||
|
|
||||||
1. **Prerequisites**
|
|
||||||
|
|
||||||
You will need the Rust toolchain installed.
|
|
||||||
|
|
||||||
- visit [rustup.rs](https://rustup.rs/) and follow the instructions for your OS.
|
|
||||||
> **WARNING!** Linux users may need build essentials (like `gcc` or `make`) installed using their package manager to compile Rust crates.
|
|
||||||
|
|
||||||
2. **Clone and Build**
|
|
||||||
|
|
||||||
Use the following code in the terminal.
|
|
||||||
`git clone https://github.com/ManOfGoldForever/Rust-Password-Manager.git`
|
|
||||||
`cd Rust-Password-Manager`
|
|
||||||
`cargo build --release`
|
|
||||||
|
|
||||||
3. **Locate Binary**
|
|
||||||
|
|
||||||
After the build finishes your executable will be located at:
|
|
||||||
|
|
||||||
- **Linux/Mac:** `target/release/pm`
|
|
||||||
- **Windows:** `target/release/pm.exe`
|
|
||||||
|
|
||||||
### Uninstallation
|
|
||||||
|
|
||||||
If for whatever reason you want to remove this perfect and amazing tool along with permanently deleting your encrypted vault:
|
|
||||||
|
|
||||||
- **Linux/Mac:** Open a terminal in the extracted folder and run: `chmod +x linux-mac-uninstall.sh && ./linux-mac-uninstall.sh`
|
|
||||||
- **Windows:** Right-click windows-uninstall.ps1 and select `Run with PowerShell`
|
|
||||||
|
|
||||||
> **WARNING!** This will delete all saved passwords, your master password, and everything else along with the tool!
|
|
||||||
|
|
||||||
### Usage
|
|
||||||
|
|
||||||
The first time you run any command you'll be prompted to set a master password.
|
|
||||||
|
|
||||||
> **IMPORTANT!** The master password is the only key to access your vault so remember it and don't lose it or you won't be able to access your data.
|
|
||||||
|
|
||||||
```rust
|
|
||||||
A simple password manager CLI
|
|
||||||
|
|
||||||
Usage: pm <COMMAND>
|
|
||||||
|
|
||||||
Commands:
|
|
||||||
add Create a new password with a name
|
|
||||||
get Get an already made password using its name
|
|
||||||
delete Delete an already made password using its name
|
|
||||||
list Lists the names of all created passwords
|
|
||||||
help Print this message or the help of the given subcommand(s)
|
|
||||||
|
|
||||||
Options:
|
|
||||||
-h, --help Print help
|
|
||||||
```
|
|
||||||
|
|
||||||
### Security and Data
|
|
||||||
|
|
||||||
All data lives in a hidden folder in your home directory if you ever need to start from scratch you could delete it:
|
|
||||||
|
|
||||||
- **Linux/Mac:** `~/.my_pass_manager`
|
|
||||||
- **Windows:** `%USERPROFILE%\.my_pass_manager\`
|
|
||||||
|
|
||||||
This tool uses Argon2id to perform some black magic. So even if someone stole your passwords.enc file from your computer, they would not be able to read it without your master password. Basically, if someone manages to steal your passwords.enc file from your computer you have bigger problems than them figuring out your passwords.
|
|
||||||
|
|
||||||
### Future Plans
|
|
||||||
|
|
||||||
I plan to add more to this eventually like maybe a secure password generator or a TUI or something, but I don't have a list of things to add yet. Maybe after using it some I will come up with some stuff we'll see what happens.
|
|
||||||
|
|
||||||
### Contributing
|
|
||||||
|
|
||||||
If you are interested in contributing to this project look at [CONTRIBUTING](./CONTRIBUTING.md) for more information on how you can.
|
|
||||||
|
|
||||||
### License
|
|
||||||
|
|
||||||
This project is licensed under the MIT License. See the [LICENSE](./LICENSE) file for details.
|
|
||||||
|
|
||||||
# Enjoy :)
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
BINARY="pm"
|
|
||||||
|
|
||||||
if [ -f "./$BINARY" ]; then
|
|
||||||
echo "Installing $BINARY to /usr/local/bin..."
|
|
||||||
sudo cp "./$BINARY" /usr/local/bin/
|
|
||||||
sudo chmod +x /usr/local/bin/$BINARY
|
|
||||||
echo "Done! You can now use 'pm' from any terminal."
|
|
||||||
else
|
|
||||||
echo "Error: Binary '$BINARY' not found. Make sure you are in the folder with the downloaded file."
|
|
||||||
fi
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
echo "Removing pm binary and encrypted data..."
|
|
||||||
sudo rm -f /usr/local/bin/pm
|
|
||||||
rm -rf ~/.my_pass_manager
|
|
||||||
echo "Uninstalled successfully."
|
|
||||||
Binary file not shown.
BIN
pm-windows.zip
BIN
pm-windows.zip
Binary file not shown.
24
release.sh
24
release.sh
|
|
@ -1,24 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
set -e
|
|
||||||
|
|
||||||
echo "Starting build process..."
|
|
||||||
|
|
||||||
echo "Cleaning up old builds..."
|
|
||||||
rm -f pm-linux-mac.tar.gz pm-windows.zip
|
|
||||||
|
|
||||||
cargo build --release
|
|
||||||
|
|
||||||
cargo build --release --target x86_64-pc-windows-gnu
|
|
||||||
|
|
||||||
echo "Packaging Linux..."
|
|
||||||
mkdir -p pm-linux-mac
|
|
||||||
cp target/release/pm linux-mac-install.sh linux-mac-uninstall.sh pm-linux-mac/
|
|
||||||
tar -czvf pm-linux-mac.tar.gz pm-linux-mac/
|
|
||||||
|
|
||||||
echo "Packaging Windows..."
|
|
||||||
mkdir -p pm-windows
|
|
||||||
cp target/x86_64-pc-windows-gnu/release/pm.exe windows-install.ps1 windows-uninstall.ps1 pm-windows/
|
|
||||||
zip -r pm-windows.zip pm-windows/
|
|
||||||
|
|
||||||
rm -rf pm-linux-mac pm-windows
|
|
||||||
echo "Done! Upload pm-linux-mac.tar.gz and pm-windows.zip to GitHub."
|
|
||||||
51
src/args.rs
51
src/args.rs
|
|
@ -3,20 +3,9 @@ use clap::{Args, Parser, Subcommand};
|
||||||
#[derive(Parser)]
|
#[derive(Parser)]
|
||||||
#[command(name = "PassManager")]
|
#[command(name = "PassManager")]
|
||||||
#[command(about = " A simple password manager CLI", long_about = None)]
|
#[command(about = " A simple password manager CLI", long_about = None)]
|
||||||
#[command(before_help = r#"
|
|
||||||
████████ █████████████ _~^~^~_
|
|
||||||
▒▒███▒▒███▒▒███▒▒███▒▒███ \) / o o \ (/
|
|
||||||
▒███ ▒███ ▒███ ▒███ ▒███ '_ - _'
|
|
||||||
▒███ ▒███ ▒███ ▒███ ▒███ / '-----' \
|
|
||||||
▒███████ █████▒███ █████
|
|
||||||
▒███▒▒▒ ▒▒▒▒▒ ▒▒▒ ▒▒▒▒▒
|
|
||||||
▒███
|
|
||||||
█████
|
|
||||||
▒▒▒▒▒
|
|
||||||
"#)]
|
|
||||||
pub struct Cli {
|
pub struct Cli {
|
||||||
#[command(subcommand)]
|
#[command(subcommand)]
|
||||||
pub command: Option<Commands>,
|
pub command: Commands,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Subcommand)]
|
#[derive(Subcommand)]
|
||||||
|
|
@ -41,6 +30,9 @@ pub struct AddPass {
|
||||||
pub struct GetPass {
|
pub struct GetPass {
|
||||||
/// The name of the password to get
|
/// The name of the password to get
|
||||||
pub name: String,
|
pub name: String,
|
||||||
|
/// Copy the password to the clipboard instead of printing it
|
||||||
|
#[arg(short, long)]
|
||||||
|
pub copy: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Args)]
|
#[derive(Args)]
|
||||||
|
|
@ -48,38 +40,3 @@ pub struct DeletePass {
|
||||||
/// The name of the password to delete
|
/// The name of the password to delete
|
||||||
pub name: String,
|
pub name: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn parses_add_command() {
|
|
||||||
let cli = Cli::try_parse_from(["pm", "add", "github"]).expect("parse add");
|
|
||||||
match cli.command {
|
|
||||||
Some(Commands::Add(add)) => assert_eq!(add.name, "github"),
|
|
||||||
_ => panic!("expected add command"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn parses_get_command() {
|
|
||||||
let cli = Cli::try_parse_from(["pm", "get", "email"]).expect("parse get");
|
|
||||||
match cli.command {
|
|
||||||
Some(Commands::Get(get)) => assert_eq!(get.name, "email"),
|
|
||||||
_ => panic!("expected get command"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn rejects_removed_copy_flag() {
|
|
||||||
let cli = Cli::try_parse_from(["pm", "get", "email", "--copy"]);
|
|
||||||
assert!(cli.is_err());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn parses_list_command() {
|
|
||||||
let cli = Cli::try_parse_from(["pm", "list"]).expect("parse list");
|
|
||||||
assert!(matches!(cli.command, Some(Commands::List)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
31
src/main.rs
31
src/main.rs
|
|
@ -11,13 +11,6 @@ use zeroize::Zeroize;
|
||||||
fn main() {
|
fn main() {
|
||||||
let cli: Cli = Cli::parse();
|
let cli: Cli = Cli::parse();
|
||||||
|
|
||||||
if cli.command.is_none() {
|
|
||||||
print_banner();
|
|
||||||
println!("Welcome to pm - The Rust Password Manager");
|
|
||||||
println!("Use 'pm --help' to see available commands.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let home_str = std::env::var("HOME")
|
let home_str = std::env::var("HOME")
|
||||||
.or_else(|_| std::env::var("USERPROFILE"))
|
.or_else(|_| std::env::var("USERPROFILE"))
|
||||||
.expect("Could not find home directory");
|
.expect("Could not find home directory");
|
||||||
|
|
@ -83,7 +76,7 @@ fn main() {
|
||||||
let mut passwords = storage::load_passwords(file_str, &encryption_key);
|
let mut passwords = storage::load_passwords(file_str, &encryption_key);
|
||||||
|
|
||||||
match &cli.command {
|
match &cli.command {
|
||||||
Some(Commands::Add(args)) => {
|
Commands::Add(args) => {
|
||||||
print!("Enter password for {} : ", args.name);
|
print!("Enter password for {} : ", args.name);
|
||||||
std::io::stdout().flush().expect("Flush failed");
|
std::io::stdout().flush().expect("Flush failed");
|
||||||
let mut p1 = read_password().expect("Failed to read password");
|
let mut p1 = read_password().expect("Failed to read password");
|
||||||
|
|
@ -102,14 +95,14 @@ fn main() {
|
||||||
p1.zeroize();
|
p1.zeroize();
|
||||||
p2.zeroize();
|
p2.zeroize();
|
||||||
}
|
}
|
||||||
Some(Commands::Get(args)) => match passwords.get(&args.name) {
|
Commands::Get(args) => match passwords.get(&args.name) {
|
||||||
Some(pw) => println!("Password for {} : {}", args.name, pw),
|
Some(pw) => println!("Password for {} : {}", args.name, pw),
|
||||||
None => println!("No password found for '{}'", args.name),
|
None => println!("No password found for '{}'", args.name),
|
||||||
},
|
},
|
||||||
Some(Commands::Delete(args)) => {
|
Commands::Delete(args) => {
|
||||||
storage::delete_password(file_str, &args.name, &encryption_key);
|
storage::delete_password(file_str, &args.name, &encryption_key);
|
||||||
}
|
}
|
||||||
Some(Commands::List) => {
|
Commands::List => {
|
||||||
if passwords.is_empty() {
|
if passwords.is_empty() {
|
||||||
println!("No passwords saved yet!");
|
println!("No passwords saved yet!");
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -120,7 +113,6 @@ fn main() {
|
||||||
println!("-----------------------");
|
println!("-----------------------");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None => unreachable!(),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
encryption_key.zeroize();
|
encryption_key.zeroize();
|
||||||
|
|
@ -129,18 +121,3 @@ fn main() {
|
||||||
v.zeroize();
|
v.zeroize();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn print_banner() {
|
|
||||||
let banner = r#"
|
|
||||||
████████ █████████████ _~^~^~_
|
|
||||||
▒▒███▒▒███▒▒███▒▒███▒▒███ \) / o o \ (/
|
|
||||||
▒███ ▒███ ▒███ ▒███ ▒███ '_ - _'
|
|
||||||
▒███ ▒███ ▒███ ▒███ ▒███ / '-----' \
|
|
||||||
▒███████ █████▒███ █████
|
|
||||||
▒███▒▒▒ ▒▒▒▒▒ ▒▒▒ ▒▒▒▒▒
|
|
||||||
▒███
|
|
||||||
█████
|
|
||||||
▒▒▒▒▒
|
|
||||||
"#;
|
|
||||||
println!("{}", banner);
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -118,74 +118,3 @@ pub fn verify_master_password(password: &str, recorded_hash: &str) -> bool {
|
||||||
.verify_password(password.as_bytes(), &parsed_hash)
|
.verify_password(password.as_bytes(), &parsed_hash)
|
||||||
.is_ok()
|
.is_ok()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
use std::time::{SystemTime, UNIX_EPOCH};
|
|
||||||
|
|
||||||
fn temp_file_path(prefix: &str) -> String {
|
|
||||||
let nanos = SystemTime::now()
|
|
||||||
.duration_since(UNIX_EPOCH)
|
|
||||||
.expect("time went backwards")
|
|
||||||
.as_nanos();
|
|
||||||
std::env::temp_dir()
|
|
||||||
.join(format!("pm_test_{}_{}_{}", prefix, std::process::id(), nanos))
|
|
||||||
.to_string_lossy()
|
|
||||||
.to_string()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn key_derivation_is_deterministic_for_same_input() {
|
|
||||||
let mut p1 = String::from("master-password");
|
|
||||||
let mut p2 = String::from("master-password");
|
|
||||||
let salt = "abc123fixedsalt";
|
|
||||||
|
|
||||||
let k1 = derive_key(&mut p1, salt);
|
|
||||||
let k2 = derive_key(&mut p2, salt);
|
|
||||||
|
|
||||||
assert_eq!(k1, k2);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn hash_and_verify_master_password_work() {
|
|
||||||
let password = "correct-horse-battery-staple";
|
|
||||||
let hash = hash_master_password(password);
|
|
||||||
|
|
||||||
assert!(verify_master_password(password, &hash));
|
|
||||||
assert!(!verify_master_password("wrong-password", &hash));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn save_load_and_delete_roundtrip() {
|
|
||||||
let path = temp_file_path("vault");
|
|
||||||
let key = [7u8; 32];
|
|
||||||
let mut data = HashMap::new();
|
|
||||||
data.insert("email".to_string(), "pw1".to_string());
|
|
||||||
data.insert("bank".to_string(), "pw2".to_string());
|
|
||||||
|
|
||||||
save_passwords(&path, &data, &key);
|
|
||||||
let loaded = load_passwords(&path, &key);
|
|
||||||
assert_eq!(loaded, data);
|
|
||||||
|
|
||||||
delete_password(&path, "email", &key);
|
|
||||||
let after_delete = load_passwords(&path, &key);
|
|
||||||
assert!(!after_delete.contains_key("email"));
|
|
||||||
assert_eq!(after_delete.get("bank"), Some(&"pw2".to_string()));
|
|
||||||
|
|
||||||
let _ = std::fs::remove_file(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn get_or_create_salt_reuses_existing_value() {
|
|
||||||
let path = temp_file_path("salt");
|
|
||||||
|
|
||||||
let first = get_or_create_salt(&path);
|
|
||||||
let second = get_or_create_salt(&path);
|
|
||||||
|
|
||||||
assert_eq!(first, second);
|
|
||||||
assert_eq!(first.len(), 32);
|
|
||||||
|
|
||||||
let _ = std::fs::remove_file(path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,12 +0,0 @@
|
||||||
$binary = "pm.exe"
|
|
||||||
|
|
||||||
if (Test-Path ".\$binary") {
|
|
||||||
$binDir = "$HOME\.cargo\bin"
|
|
||||||
if (!(Test-Path $binDir)) { New-Item -ItemType Directory -Path $binDir }
|
|
||||||
|
|
||||||
Write-Host "Installing $binary to $binDir..." -ForegroundColor Cyan
|
|
||||||
Copy-Item ".\$binary" "$binDir"
|
|
||||||
Write-Host "Done! Restart your terminal and type 'pm'." -ForegroundColor Green
|
|
||||||
} else {
|
|
||||||
Write-Host "Error: $binary not found in this folder." -ForegroundColor Red
|
|
||||||
}
|
|
||||||
|
|
@ -1,4 +0,0 @@
|
||||||
Write-Host "Uninstalling pm..." -ForegroundColor Cyan
|
|
||||||
Remove-Item "$HOME\.cargo\bin\pm.exe" -ErrorAction SilentlyContinue
|
|
||||||
Remove-Item "$HOME\.my_pass_manager" -Recurse -Force -ErrorAction SilentlyContinue
|
|
||||||
Write-Host "pm and all encrypted data have been removed." -ForegroundColor Yellow
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue