diff --git a/.gitignore b/.gitignore index 1ef352c6ed..2f909a4042 100644 --- a/.gitignore +++ b/.gitignore @@ -25,6 +25,7 @@ pyinstaller/electron/downloadloc.js pyinstaller/electron/node_modules pyinstaller/electron/package-lock.json pyinstaller/electron/dist +pyinstaller/electron/signing_logs pyinstaller/electron/fonts pyinstaller/electron/typography.css pyinstaller/electron/output.css diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index ae732b2898..fda8705e5b 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -8,8 +8,8 @@ variables: cache: # enable per-job and per-branch caching - - key: - files: + - key: + files: - ./requirements.txt prefix: "$CI_JOB_NAME" paths: @@ -25,11 +25,14 @@ before_script: - docker info || echo "no docker-command found" # Print out docker version for debugging - echo CI_PROJECT_NAMESPACE = $CI_PROJECT_NAMESPACE - echo CI_PROJECT_ROOT_NAMESPACE = $CI_PROJECT_ROOT_NAMESPACE - - python -V # Print out python version for debugging + - python -V # Print out python version for debugging - apt update - apt install -y libusb-1.0-0-dev libudev-dev # usb-support in hidapi -# This doesn't get cached in gitlab but we don't need it anyway for now: -# - ./tests/install_noded.sh --debug --elements compile + # https://github.com/python-babel/babel/issues/990#issuecomment-1760326334 + - rm -f /etc/localtime + - ln -s /usr/share/zoneinfo/Etc/UTC /etc/localtime + # This doesn't get cached in gitlab but we don't need it anyway for now: + # - ./tests/install_noded.sh --debug --elements compile - pip3 install --upgrade virtualenv - virtualenv --python=python3 .env - source .env/bin/activate @@ -45,20 +48,20 @@ check: # So effectively, gitlab is currently only used for releasing. .test: - stage: testing - # We assume here that people who want to get code into the master-branch are - # relying on PRs and people who are working on gitlab-forks are working - # on CI which probably want fast feedback on the releasing-jobs - # and therefore skip the test-job - # tem deactivated as it did not work as expected - #only: - # - $CI_PROJECT_ROOT_NAMESPACE =~ "cryptoadvance" - script: - - pip3 install -r requirements.txt - - pip3 install -e . - - pip3 install -e ".[test]" - - python3 setup.py install # compiles babel stuff as well (might make pip install obsolete) - - py.test --cov-report term --cov cryptoadvance + stage: testing + # We assume here that people who want to get code into the master-branch are + # relying on PRs and people who are working on gitlab-forks are working + # on CI which probably want fast feedback on the releasing-jobs + # and therefore skip the test-job + # tem deactivated as it did not work as expected + #only: + # - $CI_PROJECT_ROOT_NAMESPACE =~ "cryptoadvance" + script: + - pip3 install -r requirements.txt + - pip3 install -e . + - pip3 install -e ".[test]" + - python3 setup.py install # compiles babel stuff as well (might make pip install obsolete) + - py.test --cov-report term --cov cryptoadvance .test-cypress: image: registry.gitlab.com/cryptoadvance/specter-desktop/cypress-python-jammy:v9.7.0 @@ -80,7 +83,7 @@ check: release_pip: stage: releasing - only: + only: - tags script: - pip3 install -e . @@ -109,8 +112,10 @@ release_pip: release_binary_windows: stage: releasing - only: + only: - tags + variables: + GIT_DEPTH: 0 # Disable shallow clone to get all Git history tags: - windows before_script: @@ -120,17 +125,17 @@ release_binary_windows: - pip install virtualenv - virtualenv --python=python3 .env - .\.env\Scripts\activate - - pip3 install -e ".[test]" - + - pip3 install -e ".[test]" + script: # This script won't execute if the script before that fails # No need to check the version-scheme again - - echo "Releasing for ${CI_PROJECT_ROOT_NAMESPACE}" + - echo "Releasing for ${CI_PROJECT_ROOT_NAMESPACE}" - .\pyinstaller\build-win-ci.bat $CI_COMMIT_TAG - python ./utils/github.py upload ./pyinstaller/release/specterd-$CI_COMMIT_TAG-win64.zip - cd ./pyinstaller/release - - python ..\..\utils\release-helper.py sha256sums specterd-$CI_COMMIT_TAG-win64.zip > SHA256SUMS-windows + - python ..\..\utils\release_helper.py sha256sums specterd-$CI_COMMIT_TAG-win64.zip > SHA256SUMS-windows - type SHA256SUMS-windows - echo $GPG_PASSPHRASE | c:\Program` Files` `(x86`)\GnuPg\bin\gpg --detach-sign --armor --no-tty --batch --yes --passphrase-fd 0 --pinentry-mode loopback SHA256SUMS-windows artifacts: @@ -140,28 +145,31 @@ release_binary_windows: expire_in: 1 day cache: key: - files: - - ./pyinstaller/electron/package-lock.json - prefix: $CI_JOB_NAME + files: + - ./pyinstaller/electron/package-lock.json + prefix: $CI_JOB_NAME paths: - ./pyinstaller/electron/node_modules release_electron_linux_windows: image: registry.gitlab.com/cryptoadvance/specter-desktop/electron-builder:latest stage: releasing - only: + only: - tags needs: - release_binary_windows before_script: - - python3 -V # Print out python version for debugging + - python3 -V # Print out python version for debugging - apt update - apt install -y unzip libusb-1.0-0-dev libudev-dev # usb-support in hidapi - pip3 install virtualenv # Only difference to default befor_script: (ToDo fix this) - python3 -m virtualenv --python=python3 .env - source .env/bin/activate - - pip3 install -e ".[test]" + # https://github.com/python-babel/babel/issues/990#issuecomment-1760326334 + - rm -f /etc/localtime + - ln -s /usr/share/zoneinfo/Etc/UTC /etc/localtime + - pip3 install -e ".[test]" # TZ=UTC because https://github.com/nektos/act/issues/1853 script: - echo "Releasing for ${CI_PROJECT_ROOT_NAMESPACE}" - export CI_PROJECT_ROOT_NAMESPACE # needed in the build-script to download the right windows-binary @@ -176,17 +184,17 @@ release_electron_linux_windows: - ./utils/artifact_signer.sh sign --artifact ./release/SHA256SUMS-win - ./utils/artifact_signer.sh sign --artifact ./release/SHA256SUMS-linux - python3 ./utils/github.py upload ./release/Specter-Setup-${CI_COMMIT_TAG}.exe - - python3 ./utils/github.py upload ./release/specterd-${CI_COMMIT_TAG}-x86_64-linux-gnu.zip - - python3 ./utils/github.py upload ./release/specter_desktop-${CI_COMMIT_TAG}-x86_64-linux-gnu.tar.gz + - python3 ./utils/github.py upload ./release/specterd-${CI_COMMIT_TAG}-x86_64-linux-gnu.zip + - python3 ./utils/github.py upload ./release/specter_desktop-${CI_COMMIT_TAG}-x86_64-linux-gnu.tar.gz #- python3 ../utils/github.py upload ./release/SHA256SUMS-linux #- python3 ../utils/github.py upload ./release/SHA256SUMS-linux.asc #- python3 ../utils/github.py upload ./release/SHA256SUMS-win #- python3 ../utils/github.py upload ./release/SHA256SUMS-win.asc cache: key: - files: - - ./pyinstaller/electron/package-lock.json - prefix: $CI_JOB_NAME + files: + - ./pyinstaller/electron/package-lock.json + prefix: $CI_JOB_NAME paths: - ./pyinstaller/electron/node_modules @@ -204,28 +212,28 @@ release_electron_linux_windows: release_signatures: stage: post_releasing - only: + only: - tags before_script: - - python -V # Print out python version for debugging + - python -V # Print out python version for debugging - pip3 install --upgrade virtualenv - virtualenv --python=python3 .env - source .env/bin/activate - pip3 install -e ".[test]" - ./utils/artifact_signer.sh init # prepare .gnupg script: - - python3 -m utils.release-helper download # downloads the job-artifacts from gitlab - - python3 -m utils.release-helper downloadgithub # downloads additional artifacts from github (if not there and is they have SHA256SUMS-something) - - python3 -m utils.release-helper checksigs # checks the signatures of all SHA256SUMM*.asc files - - python3 -m utils.release-helper checkhashes # checks all SHA256SUM* files (might modify files on the fly due to windows line endings) - - python3 -m utils.release-helper create # creates a SHA256SUM + - python3 -m utils.release_helper download # downloads the job-artifacts from gitlab + - python3 -m utils.release_helper downloadgithub # downloads additional artifacts from github (if not there and is they have SHA256SUMS-something) + - python3 -m utils.release_helper checksigs # checks the signatures of all SHA256SUMM*.asc files + - python3 -m utils.release_helper checkhashes # checks all SHA256SUM* files (might modify files on the fly due to windows line endings) + - python3 -m utils.release_helper create # creates a SHA256SUM - ./utils/artifact_signer.sh sign --artifact ./signing_dir/SHA256SUMS # Signs the SHA256SUM - - python3 -m utils.release-helper upload_shasums # uploads SHA256SUMS to github - - python3 -m utils.release-helper upload_shasumssig # uploads SHA256SUMS.asc to github + - python3 -m utils.release_helper upload_shasums # uploads SHA256SUMS to github + - python3 -m utils.release_helper upload_shasumssig # uploads SHA256SUMS.asc to github release_docker: stage: post_releasing - only: + only: - tags before_script: - echo "Triggering Docker Release" @@ -239,46 +247,35 @@ tag_specterext_dummy_repo: only: - tags before_script: - ## Install ssh-agent if not already installed, it is required by Docker. - ## (change apt-get to yum if you use an RPM-based image) - ## - - 'which ssh-agent || ( apk update && apk add --no-cache bash git openssh )' - - docker info - - ## - ## Run ssh-agent (inside the build environment) - ## - - eval $(ssh-agent -s) - - ## - ## Add the SSH key stored in SSH_PRIVATE_KEY variable to the agent store - ## We're using tr to fix line endings which makes ed25519 keys work - ## without extra base64 encoding. - ## https://gitlab.com/gitlab-examples/ssh-private-key/issues/1#note_48526556 - ## - - echo "$SSH_SPECTEREXT_DEPLOY_KEY" | tr -d '\r' | ssh-add - > /dev/null - - ## - ## Create the SSH directory and give it the right permissions - ## - - mkdir -p ~/.ssh - - chmod 700 ~/.ssh - - ## - ## Optionally, if you will be using any Git commands, set the user name and - ## and email. - ## - - git config --global user.email "specter@secretvalues" - - git config --global user.name "specter" - - ## - ## Assuming you created the SSH_KNOWN_HOSTS variable, uncomment the - ## following two lines. - ## - - echo "$KNOWN_HOSTS" > ~/.ssh/known_hosts - - chmod 644 ~/.ssh/known_hosts - + # write access to git@github.com:cryptoadvance/specterext-dummy.git + - source ./utils/prepare_for_git_write.sh "$SSH_SPECTEREXT_DEPLOY_KEY" script: - echo "Now tagging ... git@github.com:${CI_PROJECT_ROOT_NAMESPACE}/specterext-dummy.git" - ./utils/tag_specterext_dummy.sh +update_github: + stage: post_releasing + only: + - tags + needs: + - release_signatures + before_script: + # write access to git@github.com:swan-bitcoin/specter-static.git + - source ./utils/prepare_for_git_write.sh "$SSH_SPECTERSTATIC_DEPLOY_KEY" + script: + - echo "Now updating https://github.com:${CI_PROJECT_ROOT_NAMESPACE}/specter-desktop/releases/tag/${CI_COMMIT_TAG:-v2.0.4-pre8}" + - ./utils/generate_downloadpage.sh --org_name ${CI_PROJECT_ROOT_NAMESPACE:-k9ert} --debug --version ${CI_COMMIT_TAG:-v2.0.4-pre8} generate github # default-value for testing + +update_webpage: + stage: post_releasing + only: + - tags + needs: + - release_signatures + before_script: + # write access to git@github.com:swan-bitcoin/specter-static.git + - source ./utils/prepare_for_git_write.sh "$SSH_SPECTERSTATIC_DEPLOY_KEY" + script: + - echo "Now updating https://github.com:${CI_PROJECT_ROOT_NAMESPACE}/specter-static.git" + - ./utils/generate_downloadpage.sh --org_name ${CI_PROJECT_ROOT_NAMESPACE:-k9ert} --debug --version ${CI_COMMIT_TAG:-v2.0.4-pre8} generate webpage # default-value for testing + diff --git a/.vscode/settings.json b/.vscode/settings.json index e137fadb9e..60b12ccec6 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,4 +1,9 @@ { "python.testing.unittestEnabled": false, - "python.testing.pytestEnabled": true + "python.testing.pytestEnabled": true, + "editor.formatOnSave": true, + "editor.defaultFormatter": "esbenp.prettier-vscode", + "[javascript]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, } \ No newline at end of file diff --git a/README.md b/README.md index 14d7732eba..3670209ba4 100755 --- a/README.md +++ b/README.md @@ -77,7 +77,8 @@ As a small team on a tiny budget we are working hard to make Specter better ever ### Using the Specter Desktop app The easiest way to run Specter Desktop is by installing the Specter Desktop app, which you can find on the [GitHub release page](https://github.com/cryptoadvance/specter-desktop/releases). -With this method, all you need to do is just download the right file for your operating system and install it like a normal desktop app (Debian buster is only [partially supported](https://github.com/cryptoadvance/specter-desktop/issues/769)) +With this method, all you need to do is just download the right file for your operating system and install it like a normal desktop app (Debian buster is only [partially supported](https://github.com/cryptoadvance/specter-desktop/issues/769)). +But there are a bunch of other option which you can read up in the [installation guide](docs/install_guide.md). ### Installing Specter from Pip * Specter requires Python version 3.9 to 3.10. diff --git a/docs/DeviceCreationGuide.md b/docs/DeviceCreationGuide.md new file mode 100644 index 0000000000..31574c1f2c --- /dev/null +++ b/docs/DeviceCreationGuide.md @@ -0,0 +1,179 @@ +# Device Creation Guide for Specter Hardware Wallets + +## Introduction + +Welcome to the Device Creation Guide for Specter Hardware Wallets. In this comprehensive guide, we'll walk you through the process of setting up hardware wallets within the Specter environment. Our focus is on ensuring the security of your digital assets while using various types of hardware wallets. We'll also delve into the crucial concept of derivation paths, which is essential for generating multiple keys from a single seed. Understanding these paths is key to managing and securing your Bitcoin effectively. + +## Types of Hardware Wallets + +### SD-Card Wallets + +- **Features:** SD-Card hardware wallets offer unique features that prioritize both security and portability. We'll explore these features in detail to help you understand their advantages. +- **User background:** Alice, a traveling consultant, requires a secure yet portable solution to manage her digital assets. She often moves between locations and needs a reliable way to carry her Bitcoin wallet without internet connectivity risks. +- **Use Case:** Alice opts for an SD-Card hardware wallet. Its small size and portability make it an ideal choice for her travels. She can easily carry it in her purse or securely store it in a safe. The SD-Card wallet allows her to access her digital assets on the go, without the need for an internet connection, reducing the risk of online threats. Moreover, she uses the SD-Card as a secure backup, storing a duplicate in a safe location. +- **Supported Devices:** + + - BitBox02 BitBox02 Wallet A Swiss-made hardware wallet known for its security and simplicity. It and features both SD-Card and USB interfaces for enhanced flexibility. + - Coldcard BitBox02 Wallet A popular choice for a secure and dedicated Bitcoin hardware wallet. Known for its advanced security features and ability to work with PSBT (Partially Signed Bitcoin Transactions). + - Cobo BitBox02 Wallet Designed for durability and security, Cobo is a multi-cryptocurrency hardware wallet with SD-Card support for backup and recovery. + - Passport BitBox02 Wallet This device emphasizes user-friendly design and privacy, offering air-gapped operation via QR codes and SD-Card backup. + +### QR Code Wallets + +- **Functionality:** QR code wallets operate differently, providing enhanced security through minimal direct connections with other devices. Learn how they work and why this matters. +- **User background:** Bob, a frequent user of Bitcoin for transactions, often finds himself in public places like coffee shops or conferences. He is concerned about the security risks associated with connecting his wallet to public Wi-Fi or potentially compromised devices. +- **Use Case:** Bob uses a QR Code wallet, which provides enhanced security through minimal direct connections. When making transactions, he simply scans the QR code displayed by his wallet. This method eliminates the need to connect to potentially insecure networks or devices, significantly reducing the risk of digital asset theft. The QR Code wallet’s ability to operate with minimal connectivity makes it an excellent choice for secure, hassle-free transactions in public settings. +- **Supported Devices:** + + - Jade BitBox02 Wallet A budget-friendly hardware wallet with QR code functionality for secure and offline transactions. + - SeedSigner BitBox02 Wallet An open-source project that focuses on creating a secure, offline transaction signing device using QR codes. + +### USB Wallets + +- **Characteristics:** USB wallets come with distinct features, including direct connectivity and user-friendly interfaces. Get a deeper understanding of what makes them stand out. +- **Scenarios:** Find out when and where USB wallets are your best choice. We'll showcase their versatility and compatibility with a wide range of devices. +- **User background:** Carol, a small business owner, accepts Bitcoin in her store. She needs a wallet that is both easy to use and compatible with various devices since she regularly deals with different types of transactions. +- **Use Case:** Carol chooses a USB wallet for its user-friendly interface and direct connectivity. The USB wallet's plug-and-play nature makes it simple to connect to her store's point-of-sale system or her personal computer. Its compatibility with various devices allows her to efficiently manage transactions without the need for specialized hardware. The USB wallet's intuitive interface makes it easy for Carol to navigate, making it an ideal choice for her everyday business transactions. +- **Supported Devices:** + + - BitBox02 BitBox02 Wallet Swiss-made hardware wallet, offering a blend of security and simplicity. Bitcoin Only version available. + - KeepKey Keystone Wallet A user-friendly wallet with a large display, providing a secure environment for cryptocurrency storage and transactions. + - Ledger Ledger Wallet Known for its security and sleek design, Ledger wallets support a wide range of cryptocurrencies. Less security since it contains code for other shitcoins. + - Trezor Trezor Wallet One of the first hardware wallets in the market, renowned for its ease of use and robust security measures. + - Keystone Keystone Wallet Formerly known as Cobo Vault, Keystone wallets offer a high-security solution with air-gapped QR code signing. + + +## Step-by-Step Guide for Device Creation in Specter + +### Add a new device +Select signing device +![image](https://user-images.githubusercontent.com/47259243/223428531-2f3a04d4-177d-4626-8108-b66234892541.png) +Upload public keys +![image](https://user-images.githubusercontent.com/47259243/223427859-c06faec5-78ab-4592-9ba6-4018978280cc.png) + +### Select how to connect to Bitcoin network +![image](https://user-images.githubusercontent.com/47259243/223425374-a3e68ac7-2bdb-48fe-a53b-59f235c59bd1.png) +Electrum server or... +![image](https://user-images.githubusercontent.com/47259243/223426046-dd225f00-ba18-45cb-871a-40efd7eefc1e.png) +...via Bitcoin Core node. +![image](https://user-images.githubusercontent.com/47259243/223426366-c3ba758a-34c4-4ce1-8aae-cf0cc335a892.png) + + +## Understanding Derivation Paths + +### Concept Explanation + +Understanding derivation paths is fundamental to managing the security of your digital assets. In this section, we'll provide you with an overview of what derivation paths are and why they matter. We'll also introduce key paths like BIP 44 (for multi-account hierarchy), BIP 49 (for SegWit compatibility), and BIP 84 (for native SegWit addresses). Each of these paths caters to different Bitcoin address types and plays a crucial role in organizing and securing your Bitcoin, especially within hardware wallets. + +By default, Specter Wallets are set up with: +- BIP 44 for traditional multisig wallets. +- BIP 49 or BIP 84 for SegWit singlesig wallets. + +These default settings cover the needs of most users, simplifying the wallet setup and usage process. However, understanding these paths can enhance your ability to tailor the wallet to your specific needs, especially if you have advanced security considerations. + +### Challenges and Best Practices + +1. **Complexity:** + - Derivation paths, especially when considering various Bitcoin address types like BIP 44, BIP 49, and BIP 84, can be intricate. The challenge lies in comprehending the nuances of each path and selecting the one that aligns with your specific use case. Best practice here is to educate yourself thoroughly and seek expert advice if needed. + +2. **Compatibility:** + - Using the wrong derivation path can lead to compatibility issues, making it challenging to access your funds. It's crucial to ensure that the path you choose is supported by your wallet software and the services you intend to use. Staying informed about updates and changes in the Bitcoin ecosystem is essential to avoid compatibility pitfalls. + - To assist with this, [common derivation paths for different wallets can be found at Wallets Recovery](https://walletsrecovery.org/). This resource can be useful for understanding the standard practices of various wallets and ensuring compatibility. + - Ensure that the path you choose is supported by your wallet software and the services you intend to use. + +3. **Security Risks:** + - Incorrectly managed derivation paths can introduce security risks. For instance, sharing your master public key (xpub) derived from an account with a third party may expose all the addresses generated from it. Best practice involves limiting the exposure of sensitive information and adopting a "need-to-know" approach when sharing keys or information related to derivation paths. + - Limit the exposure of sensitive information and adopt a "need-to-know" approach when sharing keys or information related to derivation paths. + +4. **Backup Strategies:** + - Derivation paths affect how you back up your wallet. Implementing a robust backup strategy that includes the derivation path information is essential. Best practice is to maintain secure backups and periodically test your recovery process to ensure you can regain access to your digital assets if the need arises. + - Implementing a robust backup strategy that includes the derivation path information is essential. + +### Example 1: BIP 84 (Hierarchical Deterministic Wallets) +#### Scenario: Multiple Account Management + +##### Context +Emily, a Bitcoin enthusiast, has diverse needs for managing her digital assets. She wants to separate her main funds from the stacking service provider she's using. For this, she needs a wallet structure that allows for clear separation while maintaining privacy and security. + +##### Use Case +Emily opts to use the BIP 84 derivation path, which is designed for native SegWit addresses, providing her with an efficient and cost-effective way to manage her Bitcoin transactions. She uses two different paths within BIP 84 to separate her funds: +- For her main wallet, where she keeps the majority of her funds, Emily uses the derivation path `m/84'/0'/0'`. This path is for her personal use, ensuring that her primary funds remain secure and private. +- For the stacking service provider, which requires her to share her extended public key (xpub) for operational purposes, she uses the derivation path `m/84'/0'/1'`. This separation allows her to maintain privacy and security, as the service provider only has visibility over the funds in the dedicated stacking account. + +##### Advantage +By using two distinct accounts under the BIP 84 standard, Emily efficiently manages her assets, keeping her main funds secure and private while still participating in stacking services. + +### Example 2: BIP 49 (SegWit Compatibility in P2SH) +#### Scenario: Balancing Efficiency with Cost in Wallet Management + +##### Context +John has been managing his Bitcoin assets using an older wallet setup. As the volume of his transactions increases, he's faced with the challenge of seeking more efficient transaction processing, both in terms of speed and reduced fees, without incurring significant costs in moving his funds. +##### Use Case +John's current wallet utilizes BIP 49, which enables SegWit compatibility through Pay to Script Hash (P2SH) addresses, identifiable by starting with '3'. His addresses follow the derivation path m/49'/0'/0'. Despite being aware of newer wallet technologies like those adhering to BIP 84, which offer even greater efficiencies by fully embracing native SegWit addresses (beginning with 'bc1'), John decides to stick with his current setup. +##### Advantage +John's decision is influenced by his desire to maintain his current UTXO set. He is cautious about the transaction fees that would be incurred in transferring his entire balance to a new wallet structure. By sticking with BIP 49, John still benefits from reduced transaction fees and improved speeds compared to legacy addresses, but he acknowledges that his setup is not as efficient as it could be with BIP 84. His choice represents a compromise between optimizing transaction efficiency and minimizing the costs associated with a complete migration to a new wallet system. + +### Example 3: BIP 84 (Native SegWit Bech32 Addresses) +#### Scenario: Maximizing Efficiency and Exploring Testnets + +##### Context +Lisa is a tech-savvy investor who keeps up with the latest developments in Bitcoin technology. She wants to use the most advanced and efficient method for managing her Bitcoin transactions. Additionally, Lisa is interested in exploring Bitcoin testnets for testing and educational purposes. + +##### Use Case +Lisa opts for a wallet that implements BIP 84, which enables the creation of native SegWit addresses that start with 'bc1'. These are Bech32 addresses, which offer benefits such as more efficient block weight usage and better error detection. For her main Bitcoin transactions, her derivation path is: `m/84'/0'/0'`. + +Moreover, Lisa is also experimenting with Bitcoin testnet. Testnets are crucial for trying out transactions without using real Bitcoin, which is an ideal environment for testing and learning. For her testnet transactions, she uses the derivation path `m/84'/1'/0'`. This path is specifically designated for testnet in BIP 84, allowing her to differentiate between real and test transactions easily. + +##### Advantage +Using BIP 84, Lisa experiences lower fees and faster transactions in her main wallet. With the addition of the testnet path, she can safely experiment and learn without risking her actual Bitcoin. This approach not only future-proofs her wallet as the industry moves towards broader adoption of SegWit but also enhances her understanding and proficiency in managing digital assets. + +### Conclusion +In each of these scenarios, the use of different derivation paths (BIP 44, BIP 49, and BIP 84) reflects a specific need and functionality in managing Bitcoin transactions: +- **BIP 44** is ideal for users like Emily, who require a structured organization for multiple types of transactions. It provides a clear hierarchical structure for different accounts under a single master seed. +- **BIP 49** benefits users like John, who seek efficiency and reduced costs in their transactions. The SegWit compatibility in P2SH format helps in lowering transaction fees and improving confirmation speeds. +- **BIP 84** is perfect for tech-savvy users like Lisa, who want to leverage the latest advancements in Bitcoin technology for optimal efficiency and future compatibility. + +For those seeking a deeper understanding of derivation paths, we recommend exploring "[Learn Me a Bitcoin". This website provides in-depth information](https://learnmeabitcoin.com/technical/derivation-paths) on the topic, and you can integrate this knowledge into our guide for a more comprehensive grasp of derivation paths. + +## Troubleshooting + +### Common Issues + +#### Check the USB Connection +- **Step 1:** Unplug the wallet from the computer. +- **Step 2:** Inspect the USB cable for any visible damage. If damaged, replace the cable. +- **Step 3:** Reconnect the wallet to the computer using a different USB port. Sometimes ports can malfunction or have poor connectivity. + +#### Restart the Wallet and Computer +- **Step 1:** Safely eject the hardware wallet from your computer. +- **Step 2:** Restart the hardware wallet. If it has a power button, turn it off and then on again. If not, disconnect and reconnect it. +- **Step 3:** Restart your computer. This can resolve issues caused by temporary software glitches. + +#### Update Wallet Firmware and Software +- **Step 1:** Check if your hardware wallet firmware is up to date. Refer to the wallet’s official website for the latest firmware version. +- **Step 2:** Update the wallet application on your computer. Ensure you're using the latest version. +- **Step 3:** After updating, reconnect the wallet and check if it is recognized. + +#### Check Device Manager (Windows) or System Report (Mac) +**For Windows:** +- **Step 1:** Open 'Device Manager'. +- **Step 2:** Look under ‘Universal Serial Bus controllers’. Check if the wallet is listed or if there are any devices with a yellow exclamation mark. +- **Step 3:** If the wallet is listed with an error, right-click on it and select ‘Update driver’. + +**For Mac:** +- **Step 1:** Click on the Apple logo and select ‘About This Mac’. +- **Step 2:** Go to ‘System Report’ and select ‘USB’. +- **Step 3:** Check if the wallet is listed under USB Device Tree. + +#### Try a Different Computer +- **Step 1:** Connect the wallet to a different computer. This can help determine if the issue is with the original computer’s hardware or software. + +#### Contact Customer Support or our Telegram Group +If none of the above steps work, the problem might be more complex or specific to the wallet. In this case, contact the customer support of the hardware wallet for further assistance. + +### Preventive Measures +- Regularly update the wallet's firmware and the computer’s software to avoid compatibility issues. +- Use high-quality USB cables and ports to ensure a stable connection. +- Avoid exposing the hardware wallet to physical damage or extreme temperatures. + +By the end of this guide, you'll be well-equipped to create and manage hardware wallets within the Specter environment and understand derivation paths. Let's get started on your journey to safeguarding your bitcoin journey. diff --git a/docs/WalletCreationGuide.md b/docs/WalletCreationGuide.md new file mode 100644 index 0000000000..9267bf74a3 --- /dev/null +++ b/docs/WalletCreationGuide.md @@ -0,0 +1,69 @@ +# Wallet Creation Guide + +## Introduction + +Specter wallets are designed to provide a user-friendly interface around Bitcoin Core, focusing on multisignature setups with airgapped signing devices, though they also support single-signature wallets. It's developed to make the interaction with Bitcoin Core more convenient for the users. + +## Types of Wallets + +### Single-Sig Wallets +- These involve one signing device or hardware wallet generating one seed. +- They are simpler and require only one secure location for seed backup. +- However, they are less secure against phishing attacks as one compromised seed can lead to lost Bitcoin. + +### Multi-Sig Wallets +- More secure than single-sig, multisig wallets require two or more signing devices to authorize transactions. +- A key feature is the quorum requirement, which specifies the number of signatures needed out of the total number of devices. For example, a common scenario is requiring 2 signatures from a total of 3 devices (2/3 quorum). This setup enhances security as it requires multiple parties or devices to agree on a transaction. +- Multisig wallets are particularly effective against phishing and theft as compromising one seed or device is not enough to access the funds. +- They offer better security, as one compromised seed doesn't result in immediate loss of funds. +- However, they are more complex to set up and require multiple secure locations for seed backups. + +#### Single-Sig Wallets Examples: + +- A user with a **Ledger Nano S** hardware wallet creating a single-signature Specter wallet for added convenience in managing their Bitcoin holdings. +- Someone who wants a simple wallet setup opting for a single-signature wallet with their **Electrum** software wallet, as they trust their computer's security. + +#### Multi-Sig Wallets Examples: + +- A group of friends setting up a multi-signature Specter wallet for a shared investment fund, where multiple devices are required to authorize transactions, enhancing security. For more detailed guidance on setting up such a wallet, [refer to our Multi-Sig Wallet Guide](multisig-guide.md). +- A Bitcoin exchange using a multi-signature setup with several hardware wallets to protect customer funds against a single point of failure. [Our Multi-Sig Wallet Guide offers](multisig-guide.md) comprehensive steps and best practices for implementing such a secure system. + + +## Step-by-Step Guide for Single-Sig Wallets + +1. **Setup and Installation** + - Download and install the Specter Desktop app from the official GitHub release page. You can [find the installation guide here](install_guide.md). + - Ensure your system meets the necessary requirements (like Python version, Bitcoin Core version). + +2. **Running the App** + - Start the Specter Desktop app. + - It should automatically detect Bitcoin Core if it's using a default data folder. + - If not, set rpcuser and rpcpassword in the Bitcoin Core's bitcoin.conf file and directly in the Specter app settings. You can [find the Node Connecting Guide here](connect-your-node.md). + +3. **Creating a Wallet** + - In Specter, navigate to the wallet creation section. + - **Important:** Before proceeding, ensure you have imported your hardware wallet device (e.g., Ledger, BitBox, Trezor) into Specter Desktop. Specter is specifically designed to enhance security by working with hardware wallets, providing an extra layer of protection compared to hot wallets. For detailed instructions on connecting your hardware wallet, [refer to the Device Creation Guide](DeviceCreationGuide.md). + - Choose the option for a single-signature wallet if you are setting up a wallet with one device. For enhanced security, consider setting up a multi-signature wallet. + - When creating a single-signature wallet, Specter will interface with your hardware wallet. **Note:** If your hardware wallet is already initialized (which is the common scenario), Specter will use the existing seed from your device to manage the wallet. There is no need to generate a new seed within Specter. It is crucial to keep your seed secure and never enter it on the computer or share it with anyone. + - **For New Hardware Wallet Users:** + If your hardware wallet is new and not yet initialized, you will need to generate a new seed as part of the hardware wallet's setup process. This is typically done directly on the hardware wallet to ensure maximum security. Follow your hardware wallet's instructions for this step. Once your hardware wallet is initialized with a new seed, you can proceed to connect it with Specter Desktop. + +4. **Backing Up the Wallet** + - After creating the wallet, it's crucial to back up the seed securely. + - For single-signature wallets, where the loss of the seed equates to the loss of funds, Specter strongly recommends storing seeds on steel. This method provides long-term durability against elements like fire and water, ensuring your backup remains intact in various scenarios. + - While still advisable for multi-signature setups, steel backups in multisig configurations are slightly less critical since the loss of a single seed doesn’t necessarily mean loss of funds, provided other signatures are available. However, it is still a best practice to secure each seed with the utmost care. + +5. **Testing the Wallet** + - It's advisable to test the wallet by sending a small amount of Bitcoin to it and then trying to access these funds using the wallet. + +## Backup PDFs + +- Specter provides backup PDFs that contain crucial information like (master) public keys and fingerprints. +- These backups do not include the seed itself. +- Keep a copy of the backup PDFs with every seed backup, but ensure they are kept private as they allow anyone to recreate the wallet and see the balance. + +## Notes + +- Always ensure the safety of your seed phrases and backup information. +- Regularly update the Specter software and your Bitcoin Core node for security and functionality improvements. +- For multisig wallet creation, a separate, more detailed guide is recommended due to its complexity. diff --git a/docs/api/README.md b/docs/api/README.md index 5562157e53..f0c95731dc 100644 --- a/docs/api/README.md +++ b/docs/api/README.md @@ -10,11 +10,13 @@ In order to make reasonable assumptions about how stable a specific endpoint is, The Specter API is using JWT tokens for Authentication. In order to use the API, you need to obtain such a token. Currently, obtaining a token is not possible via the UI but only via a special endpoint, which accepts BasicAuth (as the only endpoint). +NB: The default user and password key-value pair for Specter Desktop is `admin:admin`. + ## Curl: Create the token like this: ```bash -curl -u admin:password --location --request POST 'http://127.0.0.1:25441/api/v1alpha/token' \ +curl -u user:password --location --request POST 'http://127.0.0.1:25441/api/v1alpha/token' \ --header 'Content-Type: application/json' \ -d '{ "jwt_token_description": "A free description here to know for what the token is used", @@ -35,7 +37,7 @@ As a result, you get a json like this: The token will only be shown once. However, apart from the token itself, you can still get the details of a specific token like this: ```bash -curl -s -u admin:secret --location --request GET 'http://127.0.0.1:25441/api/v1alpha/token/4969e9fb-2097-41e7-af53-5e2082a3e4d3' | jq . +curl -s -u user:password --location --request GET 'http://127.0.0.1:25441/api/v1alpha/token/4969e9fb-2097-41e7-af53-5e2082a3e4d3' | jq . ``` ```json @@ -81,7 +83,7 @@ Here is an example of using the API with python. We don't assume that you use Ba ```python import requests -response = requests.get('http://127.0.0.1:25441/api/v1alpha/token/', auth=('admin', 'secret')) +response = requests.get('http://127.0.0.1:25441/api/v1alpha/token/', auth=('user', 'password')) json.loads(response.text) ``` diff --git a/docs/development.md b/docs/development.md index 741fd0c4e6..251ee8f642 100644 --- a/docs/development.md +++ b/docs/development.md @@ -137,9 +137,18 @@ _note: you may need to add `$HOME/.cargo/bin` to your path in `.env/bin/activate ## How to run the tests _TODO: Need more thorough tests!_ -In order to run the tests, you need bitcoind and elementsd binaries available. For Linux/Mac, there is some support for installing/compiling them. So you can: +In order to run the tests, you need bitcoind and elementsd binaries available. For Linux/Mac, there is some support for installing/compiling them. + +For bitcoind you can do: + * `./tests/install_noded.sh --bitcoin binary` will install bitcoind in tests/bitcoin + +or: + * `./tests/install_noded.sh --bitcoin compile` will compile bitcoind in tests/bitcoin + +For elementsd: + * `./tests/install_noded.sh --elements compile` will compile elements in tests/elements If you're not interested in elements, you can skip the liquid specific tests as described below. diff --git a/docs/install_guide.md b/docs/install_guide.md new file mode 100644 index 0000000000..2dbbd5a357 --- /dev/null +++ b/docs/install_guide.md @@ -0,0 +1,233 @@ +# Installation Method Decision Guide + +This guide is crafted to address the complexities and confusion users often encounter while installing Specter, a crucial tool for enhancing Bitcoin operations. Recognizing the diversity in user expertise and system requirements, a structured approach to choosing the right installation method is essential. This guide aims to streamline the decision-making process, providing clarity and direction to both novice and advanced users in their journey to effectively utilize Specter. + +## Specter Desktop Installation Methods and Their Pros and Cons + +Specter Desktop, a versatile Bitcoin wallet management application, offers multiple installation methods to accommodate various user preferences and technical skill levels. This guide outlines the available installation options, their advantages, and disadvantages to help you choose the one that best fits your needs. + + +## OS-Specific Apps for Specter Desktop + +**Ease of Installation:** OS-specific apps offer a user-friendly approach to installing Specter, making the process accessible even for those with limited technical expertise. + +**Compatibility:** These apps are tailored to work seamlessly with the operating system, ensuring optimal performance and stability. + +**Convenience:** Setting up Specter on the same machine as the Bitcoin Core node enhances convenience, as it allows for easy integration and management within a single system. + +**Targeted Updates:** OS-specific applications can receive updates and features tailored to the needs and capabilities of the specific operating system. + +## PIP Installation + +### Overview + +The PIP installation method is tailored for users who are comfortable within Python environments. It's an ideal choice for those who prefer a Python-centric approach to software installation and management. + +#### Advantages: + +- **Simplicity for Python Users:** Installation via PIP is straightforward, especially for those familiar with Python's package management. +- **Direct Control:** Users have direct control over the installation process, including version management. +- **Integration with Python Environment:** Seamlessly integrates with existing Python setups and workflows. + +#### Disadvantages: + +- **Python Knowledge Required:** Assumes familiarity with Python and its ecosystem, which might not suit all users. +- **Manual Dependency Management:** Users might need to manage dependencies manually, depending on their Python environment. + +### Ideal Use Case: + +PIP installation is best suited for users who are already working within a Python environment and are comfortable managing Python packages. This method offers a quick and efficient way to integrate Specter Desktop into existing Python-based workflows or projects. + +This addition should enhance your guide, making it more comprehensive for users with a background in Python. + +## Running Specter Desktop Using Docker + +Docker offers a robust and efficient way to install and run Specter Desktop. This method is especially beneficial for those already familiar with Docker environments. The key advantages of using Docker include: + +- **Replicability:** Docker ensures a consistent installation process, providing uniformity across different systems. This is particularly useful for users who need to deploy Specter on multiple machines or different operating systems. + +- **Ease of Setup:** Docker simplifies the installation process. By encapsulating Specter Desktop within a container, Docker manages dependencies and system-specific configurations, reducing the complexity typically associated with traditional installations. + +- **Consistent Runtime Environment:** One of Docker's main strengths is its ability to provide a stable and consistent runtime environment for applications. This consistency is crucial for maintaining stability and reliability in software operations, a key consideration for Bitcoin wallet management and operations. + +- **Isolation:** Running Specter in a Docker container ensures that it operates in an isolated environment. This isolation minimizes conflicts with other software on your system and enhances security, a vital aspect of managing Bitcoin wallets. + +By choosing Docker for installing Specter Desktop, users benefit from a streamlined, consistent, and secure installation experience, ideal for maintaining a robust Bitcoin operation environment across various platforms. + +## Integration with Node Implementations + +Specter Desktop can be seamlessly integrated with various Bitcoin node implementations, providing a comprehensive and streamlined experience for users who are already operating these nodes. This integration is especially beneficial for those looking to manage their Bitcoin wallets in conjunction with their node's functionalities. + +**Supported Node Implementations:** + +- **Raspiblitz:** Specter integrates smoothly, enhancing the node's wallet management capabilities. +- **Citadel:** This integration offers a user-friendly interface for Citadel node operators. +- **Start9:** Ideal for Start9 users seeking an integrated wallet solution. +- **Mynode:** Connects effortlessly with Mynode, offering a robust wallet management system. +- **Umbrel:** Umbrel users can enjoy a seamless integration, combining node operation with efficient wallet management. + +This approach is recommended for users who are already running these specific nodes, as it leverages the existing infrastructure to provide an integrated, efficient, and secure wallet management solution. + + +## Using Package Managers (Homebrew for macOS and Linux) + +**Advantages:** + +- Ease of Installation: Package managers streamline the installation process, making it quick and straightforward. +- Dependency Management: They automatically handle dependencies, ensuring that all required components are installed. +- Updates: You can easily update Specter Desktop with a single command, keeping your software up to date. + +**Disadvantages:** + +- Platform-Specific: This method is only available for macOS and Linux users, leaving out Windows users. +- Limited Version Control: You might not have the latest version available through the package manager if the maintainers have not updated it yet. + +## Downloading Binaries from the Specter Release Page + +**Advantages:** + +- Cross-Platform: Suitable for Windows, macOS, and Linux users, ensuring broad accessibility. +- User-Friendly: Downloading and installing binaries is typically straightforward and requires no technical expertise. +- Version Control: You have control over which version of Specter Desktop you install. + +**Disadvantages:** + +- Manual Updates: You'll need to check for updates and download new versions manually, which may be less convenient. +- Dependency Handling: Some dependencies might still need manual installation, depending on your system configuration. + +## Manual Build and Installation from Source Code (Advanced Users) + +**Advantages:** + +- Full Control: You have complete control over the build process and can customize Specter Desktop to your needs. +- Advanced Features: This method is suitable for users with technical expertise who want to contribute to the development or implement specific modifications. + +**Disadvantages:** + +- Complexity: Building from source requires a good understanding of software development, including dependency management and compiling code. +- Time-Consuming: This method can be time-consuming, especially for users not experienced with building software from source. +- Maintenance: You are responsible for keeping your installation up to date by fetching and compiling new source code. + +## Real-World Application Examples + +Here are real-world scenarios illustrating how different installation methods for Specter Desktop are chosen based on users' preferences and needs: + +1. **PIP Installation** + + **Scenario:** Alice, a data scientist, is comfortable with Python and uses it daily for her work. She prefers installing applications through Python to keep her environment consistent. She chooses PIP installation for Specter, finding it straightforward and in line with her existing Python skills. + +2. **Docker Installation** + + **Scenario:** Bob, a software developer, frequently uses Docker for his projects. He prefers containerized applications for their replicability and isolated environments. Bob opts for Docker installation for Specter, appreciating the ease of setup and consistent runtime environment it provides. + +3. **Node Implementation Integration** + + **Scenario:** Carol, an enthusiast running a Bitcoin node on Raspiblitz, wants to integrate wallet management directly with her node. She selects the integration option with Raspiblitz for a seamless experience, valuing the integrated approach and efficiency it offers. + +4. **Manual Build from Source** + + **Scenario:** Dave, a seasoned developer and Bitcoin hobbyist, seeks deep customization for his Specter setup. He is comfortable with software development and opts to build Specter from source. This method allows him the full control he desires for specific modifications and features. + + + +### Installation Method Comparison + +| Installation Method | Ease of Installation (1-5) | Customization (1-5) | Update Frequency (1-5) | Technical Expertise (1-5) | +|-----------------------|-----------------------------|---------------------|------------------------|---------------------------| +| Package Manager | 4 | 2 | 4 | 2 | +| Direct Download | 3 | 3 | 3 | 2 | +| Docker | 3 | 4 | 4 | 4 | +| Build from Source | 1 | 5 | 5 | 5 | +| PIP Installation | 2 | 3 | 4 | 3 | +| Node Implementation | 3 | 4 | 3 | 3 | + +- A score of 5 indicates the highest level of ease, customization, etc., while 1 indicates the lowest. +- This matrix is a general guideline. Specifics can vary based on the package manager and the user's technical background. + +## Installation Decision Tree +**Start Here: Choosing Your Specter Desktop Installation Method** +- Are you comfortable with technical details and customization? + - **Yes:** + - Do you require advanced customization and control? + - **Yes:** → Build from Source (Best for seasoned developers or enthusiasts seeking deep customization) + - **No:** + - Are you familiar with Docker and containerization? + - **Yes:** → Install via Docker (Ideal for consistent, isolated environments) + - **No:** → Install via Direct Download (Suitable for users comfortable with basic technical steps) + - **No:** + - Do you prefer ease of use and simplicity? + - **Yes:** + - Are you using macOS or Linux? + - **Yes:** → Use Package Manager like Homebrew (Simple and straightforward for these OS) + - **No:** → Direct Download from Specter Release Page (Easy for Windows users) + - **No:** → Use PIP (Python Package Manager) to install to any Os + +- Are you integrating with a specific Bitcoin node? + - **Yes:** + - → Choose Node-Specific Integration (Select based on the node you are operating, like Raspiblitz or Umbrel) + - **No:** → Refer back to technical comfort level and ease of use preferences + +## Installation Method Considerations + +When choosing an installation method for Specter, consider these heuristics: + +- Familiarity with Package Managers: If you are comfortable using package managers like Homebrew, they offer a convenient and straightforward installation process. +- System Constraints: Evaluate your system's limitations or constraints. Some methods may require more resources or specific system configurations. +- Need for Customization: If you require extensive customization, consider building from source, which offers the most flexibility. +- Technical Expertise: Assess your technical skill level. Less technical users might prefer simpler methods like direct downloads, while more experienced users might opt for Docker or building from source. +- Update Preferences: If staying up-to-date effortlessly is important, package managers typically make updating easier. + +## Ideal Use Case: + +This method is ideal for users who prefer a straightforward, no-hassle installation process and plan to run Specter alongside their Bitcoin Core node on the same device. It's particularly suited for those who value ease of use and system integration. + +Adding this section to your guide will provide a complete overview of all the installation methods mentioned in the PDF presentation, making it more comprehensive and useful for your readers. + +## Access Methods for Specter Desktop + +Specter Desktop offers various access methods to cater to different user needs and security preferences. These methods include: + +1. **App Access:** You can access Specter through dedicated apps available for specific operating systems. This method offers convenience and a user-friendly interface. + +2. **Local Network Access via HTTP(S):** Specter can be accessed through your local network using HTTP or HTTPS. This method is practical for users who operate Specter on a separate machine or server within their local network. + +3. **Access via Tor:** For enhanced privacy and security, Specter supports access via the Tor network. This method is ideal for users who prioritize security and wish to access their wallet remotely without exposing their real IP address. + +Each method has its unique advantages in terms of ease of use, security, and privacy. Users can choose the access method that best suits their operational environment and security requirements. + + +## Node Options for Running Specter + +Running Specter Desktop on different types of nodes has unique pros and cons that users should consider: + +1. **Full Node on Dedicated Hardware (e.g., Raspiblitz, Umbrel):** + - **Pros:** Offers robust performance and reliability, ideal for dedicated Bitcoin operations. It allows for a more secure and stable environment. + - **Cons:** Requires investment in dedicated hardware. It may be complex for beginners to set up and maintain. + +2. **Full Node on Desktop/Laptop:** + - **Pros:** Convenient for users who prefer to use existing hardware. It's a cost-effective solution without the need for additional devices. + - **Cons:** The computer's performance might be affected, and it may not be feasible to run the node continuously. There's also a higher risk of security vulnerabilities. + +3. **Pruned Node on Desktop/Laptop:** + - **Pros:** Requires less storage space, making it suitable for users with limited hardware capacity. + - **Cons:** Does not store the entire blockchain, which may limit certain functionalities and historical data access. + +Each option offers a balance of convenience, security, and functionality. Users should choose based on their technical expertise, security needs, and available resources. + +## Recent Updates +**Enhanced Electrum Integration:** Since version 2.0.0, Specter Desktop has featured integration with Electrum servers, further enhancing connectivity and accessibility options for users. Ongoing improvements in this area are expected to streamline the experience even more. + +## Future Developments of Specter Desktop + +Specter Desktop is evolving, with upcoming features and extensions that promise to enhance its functionality and user experience. Key future developments include: + +1. **Extension Framework:** Specter Desktop will support extensions, allowing users to expand its capabilities without needing to alter the core code. This will enable a more customizable experience. + +2. **New Extensions:** Planned extensions include those for connecting Specter to Swan, issuing bonds on the Liquid sidechain, importing mining rewards history from Slush Pool, fund distribution via CSV with Exfund, and building a local price database with Spotbit. + +These upcoming developments showcase Specter's commitment to growth and adaptability, catering to an expanding range of user needs and preferences in Bitcoin wallet management. + + +## Conclusion + +In conclusion, the choice of installation method for Specter Desktop depends on your technical proficiency, platform, and preferences. Package managers and binary downloads are user-friendly and suitable for most users, while Docker provides isolation and flexibility. Manual source code installation is reserved for advanced users seeking complete control and customization. Select the method that aligns with your needs to enjoy the benefits of Specter Desktop's Bitcoin wallet management capabilities. diff --git a/docs/release-guide.md b/docs/release-guide.md index 0f9b4316c1..2328e86c05 100644 --- a/docs/release-guide.md +++ b/docs/release-guide.md @@ -1,41 +1,61 @@ # Release Guide ## Creating release notes + ### Pre-requisites + - You need the correct upstream master. You should see -```bash -git remote -v | grep upstream + +```bash +git remote -v | grep upstream upstream git@github.com:cryptoadvance/specter-desktop.git (fetch) upstream git@github.com:cryptoadvance/specter-desktop.git (push) ``` + - You need a GitHub token: -If you don't have one, get one here https://github.com/settings/tokens and make sure to tick the boxes for repo and workflow as below: + If you don't have one, get one here https://github.com/settings/tokens and make sure to tick the boxes for repo and workflow as below: ![](./images/release-guide/github-token.png) Using the new token, run - ```bash - export GH_TOKEN=YOURTOKEN - ``` + +```bash +export GH_TOKEN=YOURTOKEN +``` + - You need Docker running - Checkout the master branch and ensure a clean workspace. Now, you can run + ```bash ./utils/release.sh --release-notes ``` + Or, if you want to directly set the new version: + ```bash ./utils/release.sh --new-version v1.13.1 --release-notes ``` + ## Creating a new tag + Update your master branch after the release notes PR ([example](https://github.com/cryptoadvance/specter-desktop/commit/65ff6959d7fd85cba745e4d454b30031839f857f/)) has been merged and then run: + ```bash git tag v1.13.1 && git push upstream v1.13.1 ``` + +if you have a proper setup of github- AND gitlab forks (and the remote `origin` on git pointing to your github fork), you can push that tag to origin and this is useful to test the procedures. See "CI/CD-dev-env setup" in [continuous-integration](./continuous-integration.md). + +```bash +git tag v1.13.1 && git push origin v1.13.1 +``` + ## GitLab - releasing stage -Creating a tag triggers the release process of the GitLab runners. -There exists a mirror of the GitHub repo on GitLab, but only when a tag is created on GitHub will the release part of the runners execute. You can check the status here: + +Creating a tag triggers the release process of the GitLab runners. +There exists a mirror of the GitHub repo on GitLab, but only when a tag is created on GitHub will the release part of the runners execute. You can check the status here: https://gitlab.com/cryptoadvance/specter-desktop/-/pipelines There are three stages: @@ -50,74 +70,91 @@ The first relevant stage is "releasing". Here, the Windows, Linux and pip releas - specter_desktop-v1.13.1-x86_64-linux-gnu.tar.gz The three jobs in more detail: + - release_binary_windows: is creating a binary for specterd and for Windows (Windows runner) - release_electron_linux_windows: Creates a specterd for Linux, an AppImage for Linux and an executable for Windows (Linux runner). -- release_pip: Is releasing a pypi package on [pypi](https://pypi.org/project/cryptoadvance.specter/) and creates a tarball of the pip package for the GitHub release page (Linux runner). +- release_pip: Is releasing a pypi package on [pypi](https://pypi.org/project/cryptoadvance.specter/) and creates a tarball of the pip package for the GitHub release page (Linux runner). For details look at `.gitlab-ci.yml` ## MacOS -Ideally, directly after the tag is created, start with the MacOS release. This has to be done manually, for now. There is a script for this: + +Ideally, directly after the tag is created, start with the MacOS release. As the binaries of x86/arm64 are not compatible with each other, we need to build on two MacOS architectures.This has to be done manually, for now. There is a script for this. Start with the build on x86: + +### MacOS x64 build + ```bash -./utils/build-osx.sh --version v1.13.1 --appleid "Satoshi Nakamoto (appleid)" --mail "satoshi@gmx.com" make-hash specterd electron sign upload +./utils/build-osx.sh --version v2.0.5-pre4 specterd package upload ``` -This script also runs `github.py upload `, so two more binares and the hash and signature files are uploaded to GitHub: +You can also test this procedure without messing the original project via changing the `orgName` to your `orgName` in `pyinstaller/electron/downloadloc.js`. + +This will create three artifacts on github: +* specterd-v2.0.5-pre4-osx_x64.zip +* SHA256SUMS-macos_x64 +* SHA256SUMS-macos_x64.asc + +### MacOS arm64 build + +The electron application will get built on the arm architecture. As it needs to store the sha256 hash in the electron-app, the make-hash target +will not only hash the specterd but also download the other specterd and hash it. + +```bash +./utils/build-osx.sh --version v2.0.5-pre4 --appleid "Satoshi Nakamoto (appleid)" --mail "satoshi@gmx.com" specterd make-hash electron sign package upload +``` + +This will create four artifacts on github: +* Specter-v2.0.5-pre4.dmg +* specterd-v2.0.5-pre4-osx_arm64.zip +* SHA256SUMS-macos_arm64 +* SHA256SUMS-macos_arm64.asc -- Specter-v1.13.1.dmg -- specterd-v1.13.1-osx.zip -- SHA256SUMS-macos -- SHA256SUMS-macos.asc ## GitLab - post releasing -Back to GitLab, the final stage is "post releasing". -In this stage, the invididual SHA256-hashes and signatures are combined into two final files: +Back to GitLab, the final stage is "post releasing". + +### release_signatures + +In this job, the individual SHA256-hashes and signatures are combined into two final files: + - SHA256SUMS - SHA256SUMS.asc Everything, apart from the MacOS files, are pulled from the GitLab environment, the MacOS files from GitHub. -Don't forget to delete the two MacOS files (`SHA256SUMS-macos` and `SHA256SUMS-macos.asc`) on the GitHub release page in the end. +Don't forget to delete the four MacOS files (`SHA256SUMS-macos_arm64` and `SHA256SUMS-macos_arm64.asc` and the two corresponding `_x64` files) on the GitHub release page in the end. + +This is difficult to automate as sometimes the manual steps has not succeeded while generating the SHASUM-files. As a result, those hashes are not included. So you might want to run this again. And you can, just delete the two generated files - `SHA256SUMS` and `SHA256SUMS.asc` and run the job again. + +### release_docker + +There are docker images created by the awesome [Chiang Mai LN dev](https://github.com/lncm/docker-specter-desktop). So the task of this job is to trigger their build-system which is done via `utils/trigger_docker_build.sh`. A prerequisite of this is a token in order to authenticate. That token is from Aaron, one of the maintainers of that repo, and can be found in the gitlab variables section of the CI/CD configuration. + +### tag_specterext_dummy_repo + +Sometimes there are changes on the plugin architecture. In order to create a plugin, it's quite important to know which version of the plugin system should be used. Because of that, we simply assume that the master of the [specterext-dummy](https://github.com/cryptoadvance/specterext-dummy) repo is compatible with the current master which was just tagged with the new version. +So this job will tag that repo with the same tag and the creation of a plugin will take the version into account. ## Trouble shooting + If the MacOS signatures are missing, it can happen that the following Exception will be raised: + ```bash File "/builds/cryptoadvance/specter-desktop/utils/github.py", line 295, in download_artifact raise Exception( Exception: Status-cod04 for url ... ) ``` + In any case, if the macOS binaries arrive on GitHub too late, you have to manually delete the already created `SHA256SUMS` and `SHA256SUMS.asc`, otherwise the upload to GitHub will fail if you rerun the release signatures job on GitLab - for details see ([this PR](https://github.com/cryptoadvance/specter-desktop/pull/689)). The green arrow in the screenshot is where you rerun the release signatures job on GitLab: ![](./images/release-guide/rerun-release-signatures.png) -## Editing the text on the GitHub release page -We are running a script here to create a markdown file that can be used for copy and paste. The script uses the latest release on GitHub to create links to the artifacts in the release, so it only makes sense to run it once the previous step has been completed. -- Checkout this repo: `git@github.com:cryptoadvance/corp-notes.git` -- `cd download-page` -- Run `./build.sh` - -The result `gh_page.md` can be found in the build directory. -Edit the release on GitHub and paste the md-file there along with the release notes from `release-notes.md`. - -## Website -The above script also produces html files for the website (in the same directory). The second item needs to have the release notes on GitHub, otherwise the content for the details button is missing. So, you need to re-run `./build.sh` after the release notes on GitHub are done. -- `download-page_current.html` -- `download-page_releases.html` - -Login into: https://specter.solutions/wp-login.php - -- Go to "Pages" -- Hover over "Specter Desktop - Elementor" and choose `Edit with Elementor` -- Click somewhere on area 1 (see screenshot), then somewhere on area 2, select all, delete, and paste: `download-page_current.html` - -![](./images/release-guide/website-1.png)\ -- Then click `update` -Note: If you see jinja tags, you probably pasted some templates. - -Do the same for the "Specter Releases" part, just, in this case, replace with:\ -`download-page_releases.html` - -![](./images/release-guide/website-2.png)\ +## GitHub release page and download page -You can then just exit the editing with Elementor. +This is handled by the script `./utils/generate_downloadpage.sh`. As a prerequisite, you need to clone the `specter-static` repo which contains the specter website. Clone it on the same level than specter-desktop. +Running that script will: +- install the prerequisites (basically markdown, see pyproject.toml) +- generate the GH-page and the download-page based on the `utils/templates`. +- Asks whether it should replace/update/initialize the Github Release page for the latest version +- copies over the new download-pages and asks whether it should commit/push those diff --git a/docs/release-notes.md b/docs/release-notes.md index 17f03517e6..991f32441d 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -1,16 +1,50 @@ # Release Notes +## v2.0.5 May 30, 2024 + +- Feature: Macos intel architecture oficially supported #2446 (k9ert) +- Chore: Refactoring the alias function #2439 (roshii) +- Chore: fix org_name #2450 (k9ert) +- Chore: release_helper lazy gl property #2448 (k9ert) +- Chore: release helper #2449 (k9ert) + +## v2.0.4 May 23, 2024 + +- Bugfix: Build fixes for windows #2443 (k9ert) +- Bugfix: Electron Bug Fixes (save settings, proper shutdown) #2441 (k9ert) +- Chore: Electron App Dependencies #2444 (k9ert) + +## v2.0.3 May 17, 2024 + +- Add support for more languages for mnemonics #2424 (Wim van der Ham) +- Allow bumpfee on transactions with a single output #2433 (Leon Costa) +- Docs: fix small typos and grammatical errors #2406 (JumbledUp) +- Enforce hwi init #2386 (k9ert) +- Fix Jade signing issues with Swan Vault #2421 (Manolis Mandrapilias) +- Fix install_noded.sh #2422 (Wim van der Ham) +- Fix node2 RPC port & add missing elm mark #2438 (roshii) +- Fix testnet path when deleting wallet on node #2395 (Manolis Mandrapilias) +- Kn/macos signing #2432 (k9ert) +- New documentations #1885 #2409 (j0sh21) +- Patched Fix Improperly Controlled Modification of Prototype Pollution in specter-desktop #2385 (Sergev ₱) +- Update README.md #2435 (KYC) +- adding release_notes for v2.0.2 #2384 (k9ert) +- chore: upgrade dependencies #2399 (k9ert) +- fix bad error-messaging #2437 (k9ert) +- updating dependencies #2434 (k9ert) + ## v2.0.2 September 21, 2023 + - Bugfix: Add missing signet key #2368 (Manolis Mandrapilias) - Bugfix: Jade displaying wrong multisig addresses for descriptors using multi() #2366 (Manolis Mandrapilias) - Bugfix: JSON parsing issues when copy & pasting wallet data from PDF #2355 (Manolis Mandrapilias) - Bugfix: #2319 #2330 (k9ert) -- Bugfix: fix specter.node has no _get_rpc() #2327 (k9ert) +- Bugfix: fix specter.node has no \_get_rpc() #2327 (k9ert) - Bugfix: Update spotbit api url and path #2372 (Benjamin B) - Chore(deps): Bump semver from 5.7.1 to 5.7.2 #2353 (dependabot[bot]) - Chore(deps): Bump semver from 6.3.0 to 6.3.1 in /pyinstaller/electron #2352 (dependabot[bot]) - Chore: Regex change to capture labels in wallet data imports better #2357 (Manolis Mandrapilias) -- Chore: Use prettier for Electron app #2347 (Manolis Mandrapilias) +- Chore: Use prettier for Electron app #2347 (Manolis Mandrapilias) - Chore: Optional ENFORCE_HWI_INITIALISATION_AT_STARTUP #2383 (k9ert) - Chore: remove SpecterUri #2358 (k9ert) - Chore: updating flask_babel fixes #2218 #2359 (k9ert) @@ -20,6 +54,7 @@ - Security: Fix login open redirect due to next parameter manipulation #2350 (zealsham) ## v2.0.1 March 27, 2023 + - Bugfix: Keyerror in case of frozen utxos #2308 (k9ert) - Bugfix: method getaddressinfo not implemented #2313 (k9ert) - Bugfix: replace deprecated new-window with setWindowOpenHandler #2293 (Manolis Mandrapilias) @@ -33,6 +68,7 @@ - Swan plugin: New design, improved UX and bug fixes #2309 (Manolis Mandrapilias) ## v1.14.5 January 17, 2023 + - Bugfix: autodetect from bitcoin.conf file with network set #2037 (kexkey) - Bugfix: change settings_restore implementation #2042 (k9ert) - Bugfix: fixes #1997 for Docker builds as well as command line installs #2053 (Michael Henke) @@ -51,6 +87,7 @@ - Feature: delete spectrum node #2047 (k9ert) ## v1.14.2 December 21, 2022 + - UIUX: Address labeling revamp #1978 (OTK & Manolis Mandrapilias) - UIUX: Better node selection #1987 (Manolis Mandrapilias) - UIUX: Update Passport connection instructions #2010 (BitcoinQnA) @@ -67,12 +104,14 @@ - Docs: Corrected build instructions #1996 (relativisticelectron) ## v1.14.1 December 07, 2022 + - Bugfix: Adding migrations to the hidden imports #2007 (k9ert) - Bugfix: Bump Spectrum for better error_handling (k9ert) - Chore: better error_handling and tested #2005 (k9ert) - Docs: Release guide update #2004 (Manolis Mandrapilias) ## v1.14.0 December 07, 2022 + - Feature: Spectrum addition #1952 (k9ert) - Feature: Several things for Spectrum preparation #1913 (k9ert) - Feature: QR support for Jade #1964 (Manolis Mandrapilias) @@ -104,7 +143,7 @@ - Chore: Refactoring of wallet tests #1943 (Manolis Mandrapilias) - Chore: Remove arrows and clickable headers on transactions table (for now) #1973 (OTK) - Chore: Removing the Singleton #1914 (k9ert) -- Chore: Tagging specterext-dummy with every release #1944 (k9ert) +- Chore: Tagging specterext-dummy with every release #1944 (k9ert) - Chore: Update protobuf #1933 (k9ert) - Chore: ext_wallettabs does not have to be set #1941 (Manolis Mandrapilias) - Docs: Adding sequence diagram for swan flow #1969 (Manolis Mandrapilias) @@ -112,6 +151,7 @@ - Docs: Update connect-your-node.md #1937 (cstizza) ## v1.13.1 October 17, 2022 + - Bugfix: Hover effect in balance display #1904 (Manolis Mandrapilias) - Bugfix: Remove black empty bar in tx-table after search #1912 (relativisticelectron) - Bugfix: upgrade hwi to 2.1.1 to fix #1840 #1909 (k9ert) @@ -122,6 +162,7 @@ - UIUX: Clearer unconfirmed transaction icons #1899 (relativisticelectron) ## v1.13.0 September 22, 2022 + - Feature: Improve Dev-Console #1850 (k9ert) - Feature: Menu extensions can extend menus #1648 (k9ert) - UIUX: Adding "Open Bitcoin app" verbiage to Ledger Upload Keys page #1868 (wombat6) @@ -143,6 +184,7 @@ - Chore: redirect flash call #1883 (k9ert) ## v1.12.0 August 26, 2022 + - Feature: add faucet and exfund extensions #1820 (Stepan Snigirev) - Feature: Dev tools - Adding full python access via javascript for developers #1842 (relativisticelectron) - UIUX: Complete overhaul of the tooltips used in Specter Desktop #1813 (Manolis Mandrapilias) @@ -162,10 +204,12 @@ - Docs: Supported Python versions #1847 (Willie Wheeler) ## v1.10.5 July 21, 2022 + - Bugfix: startup issues for MacOS App 1815 #1816 (relativisticelectron) - Feature: generate app_config.py in extgen #1801 (k9ert) ## v1.10.4 July 19, 2022 + - Bugfix: Better error-management for run the numbers #1790 (k9ert) - Bugfix: Consecutively uploading same file twice #1776 (relativisticelectron) - Bugfix: Fix the messed up translated html #1810 (relativisticelectron) @@ -179,12 +223,13 @@ - Docs: added necessary command to build instructions #1808 (relativisticelectron) - Docs: Mentioning Acronis as endless pacman cause #1767 (k9ert) - Feature: Add nix shell #1798 (hodlwave) -- Feature: Extensions - Making more than one Blueprint possible #1764 (k9ert) +- Feature: Extensions - Making more than one Blueprint possible #1764 (k9ert) - Feature: Rescan button for empty wallet #1779 (yogendra sankhla) - UIUX: Improve the transactions view #1746 (Manolis Mandrapilias) - UIUX: tooltips fix #1806 (relativisticelectron) ## v1.10.3 June 22, 2022 + - Bugfix: Display address on device #1774 (Manolis Mandrapilias) - Bugfix: Longer Timeout for run_the_numbers #1769 (k9ert) - Bugfix: Rescan won't work for fullnodes without explorer #1771 (k9ert) @@ -199,6 +244,7 @@ - UIUX: Fixed html character codes for multisig guide & polishment of about page #1743 (Manolis Mandrapilias) ## v1.10.2 June 03, 2022 + - Feature: Basic auth in electron #1730 (djpnewton) - Feature: Multisig beginner guide #1731 (relativisticelectron) - Bugfix: Corrected build instructions and made virtuelenv name dynamic #1735 (relativisticelectron) @@ -207,6 +253,7 @@ - UIUX: fix typo in error_logs #1739 (Bufo) ## v1.10.0 May 27, 2022 + - Feature: adding has_service() method to user #1732 (Kim Neunert) - Feature: Choose file button for labels import #1682 (Manolis) - Feature: Customised front end for adding Electrum devices #1622 (relativisticelectron) @@ -217,11 +264,11 @@ - Bugfix: add proper uid to initial config fixes #1714 #1715 (Kim Neunert) - Bugfix: better error-handling and version checks #1691 (Kim Neunert) - Bugfix: wrong arguments for requests_session #1719 (Kim Neunert) -- Chore: Adjusted releasing process and adding liquidissuer #1716 (Kim Neunert) +- Chore: Adjusted releasing process and adding liquidissuer #1716 (Kim Neunert) - Chore: Bump ejs from 3.1.5 to 3.1.7 in /pyinstaller/electron #1697 (dependabot[bot]) - Chore: Change Specter compatibility to ">=3.7,<3.10" #1707 (relativisticelectron) - Chore: Docker #1696 (Aaron Dewes) -- Chore: embit Upgrade to 0.4.13 (ripemd160 replacement) #1702 (relativisticelectron) +- Chore: embit Upgrade to 0.4.13 (ripemd160 replacement) #1702 (relativisticelectron) - Chore: install_noded.sh support for elements binary #1717 (relativisticelectron) - Chore: misc changes #1733 (Kim Neunert) - Chore: Non-standard ports for test-cypress.sh #1729 (Kim Neunert) @@ -231,8 +278,8 @@ - Chore: Introducing an issue template #1692 (Kim Neunert) - Docs: Added support for other distros (Arch/Fedora) #1713 (Ankur) - ## v1.9.4 April 28, 2022 + - UIUX: Overhaul of UTXO list, handling of locked UTXOs and scrollbar added #1580 (Manolis) - Bugfix: i18n issue preventing historical proces to work in edge cases #1664 (Kim Neunert) - Feature: More languages supported for BIP39 mnemonic import #1660 (Kim Neunert/Manolis) @@ -241,13 +288,15 @@ - Chore: Bump electron from 11.5.0 to 13.6.9 #1647 (Kim Neunert) - Chore: Bump async from 3.2.0 to 3.2.3 #1674 (dependabot[bot]) - Chore: Bump minimist from 1.2.5 to 1.2.6 in /pyinstaller/electron #1637 (dependabot[bot]) -- Chore: Updating cypress from 7.1.0 to 9.5.4 #1672 #1676 (Kim Neunert) +- Chore: Updating cypress from 7.1.0 to 9.5.4 #1672 #1676 (Kim Neunert) - Chore: Upgrade flask (and others) from 1.1.4 to 2.1.1 #1666 (Kim Neunert) ## v1.9.2 April 04, 2022 + - Bugfix: Tzdata module missing on windows fixes #1653 #1654 (Kim Neunert) ## v1.9.1 April 02, 2022 + - Bugfix: opening non-installed extensions in new window #1650 (Kim Neunert) - Bugfix: price provider uses wrong user #1640 (Kim Neunert) - Bugfix: Restore editing of labels #1649 (Kim Neunert) @@ -256,6 +305,7 @@ - Chore: Update elements to 0.21.0.2 #1641 (Kim Neunert) ## v1.9.0 March 30, 2022 + - Feature: added better scrollbar for all table-style-lists #1598 (relativisticelectron) - Feature: CLI and a publishing model for extensions #1566 (Kim Neunert) - Feature: Import of raw transaction #1591 (relativisticelectron) @@ -269,11 +319,11 @@ - UIUX: Redesign the main page #1620 (cypherhoodlum) - UIUX: Renaming and static plugin list teasering + refactoring #1569 (Kim Neunert) - UIUX: Update icon for Passport signing device #1589 (BitcoinQnA) -- Buffix: Electrum PSBT import fixes #1544 #1548 (relativisticelectron) +- Buffix: Electrum PSBT import fixes #1544 #1548 (relativisticelectron) - Bugfix: App Icon for Gnome Doc #1158 #1592 (salderma) - Bugfix: Chore - update black to fix linter failure #1642 (Kim Neunert) - Bugfix: electrum single-sig wallet import #1573 (relativisticelectron) -- Bugfix: Fixed missing vsize in tx. This led to no available feerate for RBF #1585 (relativisticelectron) +- Bugfix: Fixed missing vsize in tx. This led to no available feerate for RBF #1585 (relativisticelectron) - Bugfix: fixing the logging-system screwup #1578 (Kim Neunert) - Bugfix: logging error, caused by passing 2 arguments #1576 (relativisticelectron) - Bugfix: OAuth2_hostname #1579 (Kim Neunert) @@ -281,7 +331,7 @@ - Bugfix: refactor and fix css #1607 (Kim Neunert) - Bugfix: Release-process #1608 (Kim Neunert) - Bugfix: Reserving too many addresses at Swan, fix v2; Bugfix: admin change password #1563 (kdmukai) -- Bugfix: TXs in csv and in the UI now get blocktime for time if confirmed fixes #1552 #1559 (Kim Neunert) +- Bugfix: TXs in csv and in the UI now get blocktime for time if confirmed fixes #1552 #1559 (Kim Neunert) - Bugfix: upgrade pyinstaller #1556 (Kim Neunert) - Bugfix: versionChecker and downloadloc #1633 (Kim Neunert) - Chore: Build Script improvements #1612 (Kim Neunert) @@ -296,11 +346,11 @@ - Docs: Update docs/reverse-proxy.md #1587 (GoofyAF) - Docs: Update daemon.md #1586 (GoofyAF) - ## v1.8.0 January 29, 2022 + - Feature: Add Jade multisig support #1520 (Stepan Snigirev) - Feature: add liquid-testnet support #1527 (Stepan Snigirev) -- Feature: Allow descriptors with xpubs but without derivations (just wpkh(xpub) ) for wallet imports #1519 (Stepan Snigirev) +- Feature: Allow descriptors with xpubs but without derivations (just wpkh(xpub) ) for wallet imports #1519 (Stepan Snigirev) - Feature: search improvements #1497 (Kim Neunert) - Feature: Send dialog refactored and sped up #1454 (Kim Neunert) - Feature: Service integration - Swan #1517 (kdmukai) @@ -330,6 +380,7 @@ - Chore: Fix running bitcoind with docker #1523 (Kim Neunert) ## v1.7.2 November 30, 2021 + - Feature: Default blockchain rescan to first Taproot block for Taproot wallets #1479 (kdmukai) - Feature: Encrypted user data storage #1453 (kdmukai) - Feature: More options for address labels imports #1470 (Manolis) @@ -345,6 +396,7 @@ - Chore: Taproot test case #1482 (kdmukai) ## v1.7.1 November 05, 2021 + - Bugfix: Bitcoin Core as default for fee estimation, error handling improvements #1408 (Kim Neunert) - Bugfix: fix unknown version bug in pip-installs fixes #1442 #1450 (Kim Neunert) - Bugfix: no threading for update after creation of wallets #1457 (Kim Neunert) @@ -353,6 +405,7 @@ - Chore: Release signature process #1459 (Kim Neunert) ## v1.7.0 October 19, 2021 + - Feature: a framework for migrations and migrating single-node #1414 (Kim Neunert) - Feature: Auto privacy settings #1415 (kdmukai) - Feature: Better Error-management, logging for the APP and Macos builds improvements #1405 (Kim Neunert) @@ -365,7 +418,7 @@ - Bugfix: changing type of a device fixes #1400 #1257 #575 #1402 (Stepan Snigirev) - Bugfix: CI failure because of missing wget dependency #1375 (Kim Neunert) - Bugfix: fixes #1357 as pip3 installation of tar.gz-package was broken #1374 (Kim Neunert) -- Bugfix: Refactor wallet class fixes #1394 #1367 #1241 #1101 #1411 (Stepan Snigirev) +- Bugfix: Refactor wallet class fixes #1394 #1367 #1241 #1101 #1411 (Stepan Snigirev) - Bugfix: refactor WalletManager and Wallet, improved performance #1424 (Kim Neunert) - Bugfix: rollback embit to 0.4.5 #1379 (Kim Neunert) - Bugfix: Some liquid fixes #1401 (Stepan Snigirev) @@ -386,6 +439,7 @@ - Chore: update elements test_target to 0.21.0_rc2 #1409 (Kim Neunert) ## v1.6.0 August 27, 2021 + - Devices: Add Passport support #1343 (benk10) - Devices: Improved Liquid support for Specter-DIY #1358 (Stepan Snigirev) - Bugfix: Fix Liquid addresses UTXO count #1350 (benk10) @@ -408,6 +462,7 @@ - Chore: pytest for Electrum address label import #1363 (relativisticelectron) ## v1.5.1 August 10, 2021 + - Bugfix: Add newline char when writing bitcoin.conf setting #1325 (jeffthibault) - Bugfix: Fix specifying datadir for internal node #1315 (benk10) - Bugfix: Fix login issue for non-English mode #1321 (Kim Neunert) @@ -415,7 +470,7 @@ - Bugfix: Update rpc None error #1333 (Hani Mohammed) - Bugfix: Fix wallet PDF backup issues #1338 (benk10) - Feature: Electrum address label import #1314 (relativisticelectron) -- Translation: Update Greek translation #1317 (glowleaf) +- Translation: Update Greek translation #1317 (glowleaf) - Translation: Update Hebrew translation #1308 (Sh0ham) - Translation: Update French translation #1309 (KST-Energy) - UIUX: Add RTL languages support #1276 (benk10) @@ -425,6 +480,7 @@ - Chore: Make Specter DIY simulator connection error message more descriptive #1337 (Tushar Singla) ## v1.5.0 July 16, 2021 + - Feature: Translation (Babel) integration and initial translations #1247 (kdmukai, 2a3dex, rafa1239, Volker Herminghaus, glowleaf, DirkVdk, mutatrum, Kryptoministern, PommbearBTC, Gummybear, sreshta suresh, KST-Energy, alltheseas, dudezoo, Sergei Tikhomirov, Bitpaint) - Feature: Api framework #1232 (Kim Neunert) - Bugfix: Delete raw transactions when wallet is deleted #1300 (Stepan Snigirev) @@ -457,6 +513,7 @@ - Liquid: TxList and AddressList classes for Liquid #1280 (Stepan Snigirev) ## v1.4.6 June 28, 2021 + - Feature: Add Keystone device #1237 (Stepan Snigirev) - Feature: Export device types and labels #1226 (kdmukai) - Feature: Liquid - asset support #1216 (Stepan Snigirev) @@ -473,6 +530,7 @@ - Chore: Refactor wallet creation to Wallet.create method #1242 (Stepan Snigirev) ## v1.4.5 June 16, 2021 + - Feature: Add Blockstream Jade support #1234 (benk10) - Feature: Add SeedSigner device #1225 (Stepan Snigirev) - Feature: Show Liquid icon on node manager #1224 (benk10) @@ -482,21 +540,24 @@ - Bugfix: "NameError: name 'protocol' is not defined" #1229 (DerM007) - Chore: Elements testing #1212 (Kim Neunert) - ## v1.4.3 June 10, 2021 + - Bugfix: add some liquid fixes #1194 (Stepan Snigirev) - Feature: Fully unblind liquid transactions sent by Specter #1220 (Stepan Snigirev) ## v1.4.2 June 01, 2021 + - Bugfix: unconventional derivations fail regexes #1204 (Stepan Snigirev) - Chore: Simplify emptiness checks in txlist #1202 (Roman Zeyde) ## v1.4.1 May 30, 2021 + - Bugfix: a few typos #1201 (Roman Zeyde) - Bugfix: Set http as default network protocol if non specified #1199 (benk10) - Bugfix: Update Tor circuit every request #1200 (benk10) ## v1.4.0 May 28, 2021 + - Feature: Broadcast transactions over block explorer via Tor #1183 (benk10) - Feature: "Verify on device" with Coldcard sugggest using airgap instead of USB connection #1157 (Manolis) - Feature: Merge in Liquid branch (preparation for Liquid support) #1174 (benk10) @@ -525,13 +586,14 @@ - Chore: Upgrade internal bitcoind version to v0.21.1 #1173 (benk10) ## v1.3.1 April 26, 2021 -- Feature: Add unconfirmed tx fees data #1085 (benk10) + +- Feature: Add unconfirmed tx fees data #1085 (benk10) - Feature: Use RPCAuth instead of username + password #1093 (benk10) - Feature: Return to/ cancel setup process #1091 (benk10) - UIUX: Show user-friendly error messages when session expires #1087 (benk10) - UIUX: Use Bitcoin icon set #1080 (benk10) - Docs: Update faq.md #1123 (Kim Neunert) -- Docs: added build-instructions #1077 (Kim Neunert) +- Docs: added build-instructions #1077 (Kim Neunert) - Bugfix: BitBox02 timeout issue #1090 (benk10) - Bugfix: Fix crash if estimatesmartfee fails #1086 (benk10) - Bugfix: typo/exception #1102 (djpnewton) @@ -552,6 +614,7 @@ - Chore: Refactor setup wizard #1120 (benk10) ## v1.3.0 March 29, 2021 + - Bugfix: bump embit version, add secp binary #1031 (Stepan Snigirev) - Bugfix: consolidations issue #1034 (benk10) - Bugfix: Default bitcoind timeout to 60s for all platforms #1044 (kdmukai) @@ -599,6 +662,7 @@ - UIUX: Only tor quicksync warning #1054 (Kim Neunert) ## v1.2.0 February 20, 2021 + - Bugfix: a minor bug that always shows address as used #927 (jleo84) - Bugfix: cypress-tests #961 (Kim Neunert) - Bugfix: Fix key initial format in wallet info #925 (benk10) @@ -615,15 +679,16 @@ - UIUX: Preserve form status when creating a transaction #938 (djpnewton) ## v1.1.0 January 30, 2021 + - Bugfix: #784 URL encode to prevent breaking characters on RPC connection #866 (Maxi Dev) -- Bugfix: #829 - font size and horizontal alignment #900 (Patrick) +- Bugfix: #829 - font size and horizontal alignment #900 (Patrick) - Bugfix: Check whether tx address contains list before enumerating it #855 (Ondrej Calda) - Bugfix: Fix #605 - Display UI notification after btc core connection test #912 (Patrick) - Bugfix: Fix key purpose labeling being overwritten #887 (benk10) - Bugfix: Fix no block height with Bitcoin Core v0.19 #859 (benk10) - Bugfix: Fix tx info showing wrong input address #865 (benk10) - Bugfix: ugly fix utxo blockexplorer rescan #897 (Stepan Snigirev) -- Bugfix: Util testing and small bugfix about multisig treshold #698 (Manolis) +- Bugfix: Util testing and small bugfix about multisig treshold #698 (Manolis) - Chore: Bump electron from 10.1.3 to 10.2.0 in /pyinstaller/electron #916 (dependabot[bot]) - Devops: Added PyCharm IDE configuration + fixed DEVELOPMENT.md title level hierachy (incl. TOC) #892 (paeet) - devops: Fixed deprecation warnings #894 (Patrick) @@ -644,7 +709,7 @@ - Feature: Replace address list on receive tab with addresses tab #914 (benk10) - Feature: SLIP-132 switch for PDF backup key format #849 (Maxi Dev) - Feature: Support Cobo single key files #915 (benk10) -- UIUX: Add target="_blank" for help links #911 (benk10) +- UIUX: Add target="\_blank" for help links #911 (benk10) - UIUX: fixed typos in wallet_receive.jinja and wallet_settings.jinja #853 (Zach Zager) - UIUX: Fixes - TX table toolbar alignment, Network label alignment #899 (Patrick) - UIUX: Fix typo (puropse -> purpose) #898 (Stacie) @@ -653,10 +718,11 @@ - UIUX: Make `Add Keys` more obvious #884 (Franck Royer) ## v0.11.0-pre1 December 21, 2020 + - Feature: Tor settings and tor_only mode #765 (benk10) - Feature: adding automatic ssl-creation #789 (Kim Neunert) -- Feature: Export CSV data functionality for all table-data with optional historical price data #758 (benk10) -- Feature: Refactor History Tab and Search in history #760 (benk10) +- Feature: Export CSV data functionality for all table-data with optional historical price data #758 (benk10) +- Feature: Refactor History Tab and Search in history #760 (benk10) - Feature: Cypress Frontend-testing #712 (Kim Neunert) - docs: fixes #769 #773 (Kim Neunert) - Bugfix: Remove Notification instead of logging out #755 #791 (benk10) @@ -666,6 +732,7 @@ - UIUX: Switched Address to Label #720 #764 (figgyfigs) ## v0.10.2 December 5, 2020 + - HWI: Update udev rules for Specter DIY (#742) (@stepansnigirev) - HWI: Update communication with Specter DIY (#746) (@stepansnigirev) - Bugfix: Fix RBF for UTXO consolidation and wrong fee on import tx (#744) (@ben-kaufman) @@ -675,7 +742,7 @@ - Bugfix: Remove listwalletdir call from wallet manager checker (#734) (@stepansnigirev) - Bugfix: Fix Trezor and Keepkey multisig address verification (#733) (@stepansnigirev) - Bugfix: Show QR code address verification only for Specter-DIY (#714) (@ben-kaufman) -- Bugfix: Fix typo with links (target="blank" => target="_blank")(#703) (@stepansnigirev) +- Bugfix: Fix typo with links (target="blank" => target="\_blank")(#703) (@stepansnigirev) - Bugfix: Add .python-version to .gitignore (#699) (@Sjors) - Bugfix: Allow renaming admin account and a few bug fixes (#732) (@ben-kaufman) - Bugfix: Fix typo:manual configuration (#739) @@ -692,6 +759,7 @@ - Docs: Update FAQ on how to upgrade Specter Desktop (#702) (@k9ert) ## v0.10.0 November 21, 2020 + - HWI: Fix integration with Ledger running the latest Bitcoin app (1.5.1) (#650) (@ben-kaufman) - HWI: Fix BitBox02 integration (#652) (@ben-kaufman) - Bugfix: Fix spelling mistake (#589) (@danielnordh) @@ -776,6 +844,7 @@ - Docs: Update FAQ on Coin Control (@moritzwietersheim) (#563) ## v0.8.1 October 1, 2020 + - Bugfix: Fix displaying address on BitBox02 (#416) (@stepansnigirev) - Bugfix: Fix Specter not starting up if Ledger is connected and asleep (#416) (@stepansnigirev) - Bugfix: Fix wallet-create wizard ignoring passphrase on Trezor One and KeepKey (#420) (@ben-kaufman) @@ -805,6 +874,7 @@ - Docs: Add a note to the README about setting `server=1` for Bitcoin Core GUI (#459) (@Maple44) ## v0.8.0 September 19, 2020 + - Build: Refactor the desktop app to run Specter in a separate thread instead of from binaries (#370) (@stepansnigirev) - Devices: [BitBox02](https://shiftcrypto.ch/bitbox02/) single-sig support (#393, #402) (@ben-kaufman, @stepansnigirev) - Bugfix: Fix file uploading (#327) (@ben-kaufman) @@ -850,17 +920,20 @@ - Docs: Add Help Wanted section to the README (#357) (@moritzwietersheim) ## v0.7.2 August 28, 2020 + - Build: Add build scripts for building the release files (#319) (@stepansnigirev) -- Bugfix: In the desktop app, open a remote https/ Tor Specter node in browser instead of Qtweb (#320) (@stepansnigirev) +- Bugfix: In the desktop app, open a remote https/ Tor Specter node in browser instead of Qtweb (#320) (@stepansnigirev) - Bugfix: Fix specterd not shutting down after closing desktop app with `cmd+q` (#320) (@stepansnigirev) - Bugfix: Fix send amount using sats units by default even when using BTC as wallet unit (#318) (@ben-kaufman) - Bugfix: Fix redirect error after login (#318) (@ben-kaufman) ## v0.7.1 August 26, 2020 + - Bugfix: Move docker dependency away from main code (#316) (@stepansnigirev) ## v0.7.0 August 26, 2020 -- Build: Create Specter Desktop app (#273, #308) (@stepansnigirev, @ben-kaufman) + +- Build: Create Specter Desktop app (#273, #308) (@stepansnigirev, @ben-kaufman) - Bugfix: Fix importing PSBT functionality (#291) (@ben-kaufman) - Bugfix: Fix issue with using xpub with no derivation (`m` only) (#282) (@hodlwave) - Bugfix: Fix issue with uploading PSBT from SD card (#292) (@luclefleur) @@ -880,6 +953,7 @@ - Docs: Update README and screenshots (#309) (@ben-kaufman) ## v0.6.1 August 13, 2020 + - Devices: Add animated QR codes support for [Cobo Valut](https://cobo.com/hardware-wallet/cobo-vault) (#279) (@stepansnigirev) - Bugfix: Fix malformed JSON issue when exporting wallet to file in Chrome (#274) (@ben-kaufman) - Bugfix: Fix issue with importing wallet using a key with no origin derivation data (#274) (@ben-kaufman) @@ -889,10 +963,11 @@ - HWI: Trezor and KeepKey change address verification (#276) (@stepansnigirev) - UI: New export options for keys of devices (#275) (@ben-kaufman) - UI: Add toggle device passphrase option when adding a Trezor or KeepKey device (#278) (@ben-kaufman) -- Refactoring: Restructure the device classes to improve modularity and flexibility (#276) (@stepansnigirev) -- Refactoring: Improve HWI detect device speed by enumerating only on the specific type needed (#280) (@stepansnigirev) +- Refactoring: Restructure the device classes to improve modularity and flexibility (#276) (@stepansnigirev) +- Refactoring: Improve HWI detect device speed by enumerating only on the specific type needed (#280) (@stepansnigirev) ## v0.6.0 August 4, 2020 + - Build: Create `specterd` and `hwibridge` binaries (#258, #271) (@stepansnigirev) - Devices: [Cobo Valut](https://cobo.com/hardware-wallet/cobo-vault) multisig support (#268) (@stepansnigirev) - Bugfix: Fix issues and improve performance by removing local caching (#242) (@ben-kaufman) @@ -924,6 +999,7 @@ - Test: Fix test issues due to nondeterministic order of tests (#250) (@k9ert) ## v0.5.5 July 15, 2020 + - Devices: Support Electrum wallet as a device (#222) (@stepansnigirev) - Devices: Support Generic device (usable for any PSBT compatible device not directly supported in Specter) (#221) (@stepansnigirev) - Bugfix: Fix crash when creating a transaction from wallet with a device with type "Other" (#221) (@stepansnigirev) @@ -940,6 +1016,7 @@ - UI: Improved amount validation in new transaction screen (#221) (@stepansnigirev) ## v0.5.4 July 13, 2020 + - Devices: (⚠️ Experimental) Support Bitcoin Core hot wallets (#210) (@ben-kaufman) - Bugfix: Fix issues with Bitcoin Core calls timing out (#214) (@stepansnigirev) - Bugfix: Fix issues with non standard keys (#209) (@stepansnigirev) @@ -948,6 +1025,7 @@ - UI: Improve keys table (#218) (@stepansnigirev) ## v0.5.3 July 10, 2020 + - Bugfix: Fix potential crashes and issues due to multi-threading race conditions (#205) (@stepansnigirev) - Bugfix: Fix crash if current Specter version could not be obtained (#202) (@stepansnigirev) - Bugfix: Fix potential issue with wallets not being properly loaded (#197) (@ben-kaufman) @@ -960,6 +1038,7 @@ - UI: Indicate selected device on the sidebar (#206) (@stepansnigirev) ## v0.5.2 July 5, 2020 + - Devices: [Cobo Valut](https://cobo.com/hardware-wallet/cobo-vault) single-sig support (#189) (@stepansnigirev) - Devices: Support Specter-DIY [v1.2.0](https://github.com/cryptoadvance/specter-diy/releases/tag/v1.2.0) (#188) (@stepansnigirev) - Bugfix: Fix issue with wallets and devices not being loaded properly (#190) (@ben-kaufman) @@ -969,9 +1048,11 @@ - UI: Improve sidebar UI when Bitcoin Core is not connected or not configured (#184) (@stepansnigirev) ## v0.5.1 (v0.5.0 Hotfix) June 30, 2020 + - Bugfix: Fix issue with running Specter after installing from pip (@stepansnigirev) ## v0.5.0 June 30, 2020 + - Bugfix: Fix compatibility issue with latest Ledger and Trezor firmwares (addresses new BIP143 vulnerability), use HWI 1.1.2 (#178) (@stepansnigirev) - Bugfix: Don't update explorer if chain is unknown (#174) (@stepansnigirev) - Bugfix: Fix labels issue with Bitcoin Core v0.20.0 (#160) (@ben-kaufman) @@ -998,6 +1079,7 @@ - Docs: README updates (#164) (@moritzwietersheim) ## v0.4.0 May 31, 2020 + - Mobile friendly UI (#112) (@stepansnigirev) - Showing transacation details while sending (#130) (@ben-kaufman) - Being able to copy transaction instead of sending via own node (#232) (@ben-kaufman) @@ -1007,6 +1089,7 @@ - A lot of refactorings (especially for template-logic) and tidyups. We also removed some dependencies (@ben-kaufman, @stepansnigirev) ## v0.3.0 May 11, 2020 + - QR-Code animations enable to pass more information in smaller chunks (#104) (@gorazdko) - Renaming and Deleting wallets (#108) (@ben-kaufman) - addresses and utxo-view for better overview of your funds (#95) (@ben-kaufman) @@ -1017,22 +1100,27 @@ - Windows support (#127) (@stepansnigirev) ## v0.2.0 Mar 27, 2020 + - label addresses to get remember where coins are coming from (#94) (@ben-kaufman) - Optional Authentication with RPC Password (#81) (@k9ert) - Support custom block explorer for all networks (@ben-kaufman) ## v0.1.2 Mar 6, 2020 + - bugfix-release (#84) ## v0.1.1 Feb 29, 2020 + - Support for compressed PSBT in QR-codes #80 (@stepansnigirev) - Use specter-diy to sign via USB (#77) (@stepansnigirev) ## v0.1.0 Feb 27, 2020 + - Rescan Blockchain to import older wallets easily (#73) (@stepansnigirev) - Command-line options for server: daemon, ssl-certs and tor ## v0.0.2 Feb 20, 2020 + - First PIP-Release available on [PyPi](https://pypi.org/project/cryptoadvance.specter/#history) (#69) (@k9ert) - HWI support enables a whole bunch of hardwarewallets to work with specter (#23) (@kdmukai) - Tor integration (#19) (@kdmukai) @@ -1040,5 +1128,6 @@ - https support (#64) (@stepansnigirev) ## v0.0.1-alpha Sep 28, 2019 + Specter Desktop has been started by @stepansnigirev since Aug 30, 2019. Thank you Stepan :-). diff --git a/docs/self-signed-certificates.md b/docs/self-signed-certificates.md index ce0235a37e..7b3373021b 100644 --- a/docs/self-signed-certificates.md +++ b/docs/self-signed-certificates.md @@ -1,22 +1,23 @@ # SSL Certificate + ## Why a certificate is important Browsers require secure communication with the server to use camera API. Without it we can't use QR code scanning. -If you are running a VPS it's easy - you just [issue a new certificate](./reverse-proxy#adding-https) with Letsencrypt. +If you are running a VPS it's easy - you just [issue a new certificate](./reverse-proxy.md#adding-https) with Letsencrypt. If you are only using the node at home and want to use it from your local network and via camera, you need to run it via SSL. ## Easy solution The easiest solution is to simply add `--ssl` to the serve-command and the certificate will get created automatically in the specter-home-folder. + ``` python3 -m cryptoadance.specter server --ssl ``` ## Manual creation - A second way, which provides more customization, is to run the [`gen-certificate.sh`](gen-certificate.sh) script in this folder with your node's IP address as an argument: ```sh @@ -33,7 +34,7 @@ Provide these files to Specter as arguments: python -m cryptoadvance.specter server --cert=./cert.pem --key=./key.pem ``` -*Note:* Adding `--tor=your-tor-password` will create a tor hidden service with https. +_Note:_ Adding `--tor=your-tor-password` will create a tor hidden service with https. ### Specter with Nginx @@ -53,7 +54,7 @@ The config should look like this: server{ listen 80 default_server; listen 443 ssl http2; - + server_name your_domain_or_ip; ssl_certificate /etc/ssl/certs/cert.pem; diff --git a/mkdocs.yml b/mkdocs.yml index ebb4214221..42c48b496c 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -8,6 +8,9 @@ nav: - multisig-guide.md - User Guide: - faq.md + - install_guide.md + - DeviceCreationGuide.md + - WalletCreationGuide.md - 'Using Tor': tor.md - 'Signing messages': sign-message.md - Operating Guide: diff --git a/pyinstaller/build-win-ci.bat b/pyinstaller/build-win-ci.bat index d13ad0159e..21820e4081 100755 --- a/pyinstaller/build-win-ci.bat +++ b/pyinstaller/build-win-ci.bat @@ -14,18 +14,13 @@ call .\.buildenv\Scripts\activate echo " --> Installing test-requirement" pip3 install -e ".[test]" -echo " --> Setting version in setup.py" -python .\utils\release-helper.py set_setup_py_version %1% - - echo " --> Building pypi package" pip3 install build==0.10.0 python -m build echo " --> Installing pypi package" -python .\utils\release-helper.py install_wheel %1% +python .\utils\release_helper.py install_wheel %1% -pip3 install -r pyinstaller/test_requirements.txt cd pyinstaller Rem This file gets further packaged up with the pyinstaller and will help specter to figure out which version it's running on diff --git a/pyinstaller/electron/basic_auth.html b/pyinstaller/electron/basic_auth.html index 80a2c7ac82..957fecb0f9 100644 --- a/pyinstaller/electron/basic_auth.html +++ b/pyinstaller/electron/basic_auth.html @@ -33,8 +33,9 @@

The server at '...' is prompting for authorization< const fs = require('fs') const { ipcRenderer } = require('electron') const helpers = require('./helpers') - const getAppSettings = helpers.getAppSettings - const appSettingsPath = helpers.appSettingsPath + const config = require('./config') + const getAppSettings = config.getAppSettings + const appSettingsPath = config.appSettingsPath function cancel() { window.close(); diff --git a/pyinstaller/electron/build/entitlements.mac.plist b/pyinstaller/electron/build/entitlements.mac.plist index 16ee8e8fd7..7e5286745e 100644 --- a/pyinstaller/electron/build/entitlements.mac.plist +++ b/pyinstaller/electron/build/entitlements.mac.plist @@ -2,6 +2,8 @@ + com.apple.security.cs.allow-jit + com.apple.security.cs.allow-unsigned-executable-memory com.apple.security.device.camera diff --git a/pyinstaller/electron/downloadloc.js b/pyinstaller/electron/downloadloc.js index b4e55875ba..05d1390e4c 100644 --- a/pyinstaller/electron/downloadloc.js +++ b/pyinstaller/electron/downloadloc.js @@ -1,6 +1,14 @@ +function orgName() { + // This can be changed in order to make download possible from other github orgs + return "cryptoadvance" +} + function getDownloadLocation(version, platformname) { - return `https://github.com/cryptoadvance/specter-desktop/releases/download/${version}/specterd-${version}-${platformname}.zip` + if (platformname != "osx") { + return `https://github.com/${orgName()}/specter-desktop/releases/download/${version}/specterd-${version}-${platformname}.zip` + } + return `https://github.com/${orgName()}/specter-desktop/releases/download/${version}/specterd-${version}-${platformname}_${process.arch}.zip` } function appName() { @@ -8,7 +16,8 @@ function appName() { } module.exports = { - getDownloadLocation: getDownloadLocation, - appName: appName + getDownloadLocation, + appName, + orgName } diff --git a/pyinstaller/electron/error_logs.html b/pyinstaller/electron/error_logs.html index c1cc6bd41a..a9fa6ac09a 100644 --- a/pyinstaller/electron/error_logs.html +++ b/pyinstaller/electron/error_logs.html @@ -1,23 +1,20 @@ - - -
- This window shows you the last 700 lines Logs of the specterApp. This also includes - the Logs of specterd which are marked as such. It might give you hints on why specter is not coming up properly. - The best approach is to scroll to the bottom and then search upwards for errors. - You can find the logfile in [yourHomedirectory]/.specter/specterApp.log. -
-
- -
+ + +
+ This window shows you the last 700 lines Logs of the specterApp. This also includes the Logs of specterd which are + marked as such. It might give you hints on why specter is not coming up properly. The best approach is to scroll to the + bottom and then search upwards for errors. You can find the logfile in [yourHomedirectory]/.specter/specterApp.log. +
+
+
- - \ No newline at end of file + + diff --git a/pyinstaller/electron/helpers.js b/pyinstaller/electron/helpers.js deleted file mode 100644 index cb9c8392be..0000000000 --- a/pyinstaller/electron/helpers.js +++ /dev/null @@ -1,125 +0,0 @@ -const fs = require('fs') -const os = require('os') -const path = require('path') -const crypto = require('crypto') -const readLastLines = require('read-last-lines'); -const downloadloc = require('./downloadloc'); -const appName = downloadloc.appName() -const appNameLower = appName.toLowerCase() -const isDev = process.env.NODE_ENV === "development" -const unresolvedDevFolder = process.env.SPECTER_DATA_FOLDER || "~/.specter_dev" -const devFolder = unresolvedDevFolder.replace(/^~/, os.homedir()); -const prodFolder = path.resolve(os.homedir(), `.${appNameLower}`) -const isMac = process.platform === 'darwin' - -let appSettingsPath -let specterdDirPath -let specterAppLogPath -let versionData - -// Use different version-data.jsons - -// Should look like this: -// { -// "version": "v2.0.0-pre32", -// "sha256": "aa049abf3e75199bad26fbded08ee5911ad48e325b42c43ec195136bd0736785" -// } - -if (isDev) { - let versionDataPath = `${devFolder}/version-data.json` - try { - versionData = require(versionDataPath) - } - catch { - } -} -else { - try { - versionData = require('./version-data.json') - } - catch (e) { - console.log('Could not find default version data configurations: '+e) - versionData = { - "version": "", - "sha256": "" - } - } -} - -if (isDev) { - appSettingsPath = `${devFolder}/app_settings.json` - specterdDirPath = `${devFolder}/specterd-binaries` - specterAppLogPath = `${devFolder}/specterApp.log` -} -else { - appSettingsPath = `${prodFolder}/app_settings.json` - specterdDirPath = `${prodFolder}/specterd-binaries` - specterAppLogPath = `${prodFolder}/specterApp.log` -} - -function getFileHash(filename, callback) { - let shasum = crypto.createHash('sha256') - // Updating shasum with file content - , s = fs.ReadStream(filename) - s.on('data', function(data) { - shasum.update(data) - }) - // making digest - s.on('end', function() { - var hash = shasum.digest('hex') - callback(hash) - }) -} - -function getAppSettings() { - let defaultSettings = { - mode: 'specterd', - specterURL: 'http://localhost:25441', - basicAuth: false, - basicAuthUser: '', - basicAuthPass: '', - tor: false, - proxyURL: "socks5://127.0.0.1:9050", - specterdVersion: (versionData && versionData.version !== undefined) ? versionData.version : 'unknown', - specterdHash: (versionData && versionData.sha256 !== undefined) ? versionData.sha256 : 'unknown', - specterdCLIArgs: "", - versionInitialized: false - } - - try { - if (!fs.existsSync(appSettingsPath)){ - fs.mkdirSync(path.resolve(appSettingsPath, '..'), { recursive: true }); - } - fs.writeFileSync(appSettingsPath, JSON.stringify(defaultSettings), { flag: 'wx' }); - } catch { - // settings file already exists - } - - // Make sure to add missing settings in case the format changed or new settings were added - let appSettings = require(appSettingsPath) - for (let key of Object.keys(defaultSettings)) { - if (!appSettings.hasOwnProperty(key)) { - appSettings[key] = defaultSettings[key] - } - } - - return appSettings -} - -function getSpecterAppLogs(callback) { - readLastLines.read(specterAppLogPath, 700) - .then(callback); -} - -module.exports = { - getFileHash: getFileHash, - appSettingsPath: appSettingsPath, - getAppSettings: getAppSettings, - specterdDirPath: specterdDirPath, - getSpecterAppLogs: getSpecterAppLogs, - specterAppLogPath: specterAppLogPath, - versionData, - isDev: isDev, - devFolder, - isMac, isMac -} diff --git a/pyinstaller/electron/main.js b/pyinstaller/electron/main.js index e4f59e193d..884d82e38d 100644 --- a/pyinstaller/electron/main.js +++ b/pyinstaller/electron/main.js @@ -1,30 +1,18 @@ // Modules to control application life and create native browser window -const { app, nativeTheme, nativeImage, BrowserWindow, Menu, Tray, screen, shell, dialog, ipcMain } = require('electron') - -const path = require('path') const fs = require('fs') -const { URL } = require('node:url') -const request = require('request') -const https = require('https') -const extract = require('extract-zip') -const defaultMenu = require('electron-default-menu') -const ProgressBar = require('electron-progressbar') const { spawn, exec } = require('child_process') -const { - getFileHash, - getAppSettings, - appSettingsPath, - specterdDirPath, - specterAppLogPath, - versionData, - isDev, - devFolder, - isMac, -} = require('./helpers') +const { app, nativeImage, BrowserWindow, Menu, screen, shell, dialog, ipcMain } = require('electron') +const defaultMenu = require('electron-default-menu') +const contextMenu = require('electron-context-menu') + +const { appSettingsPath, specterdDirPath, appSettings, platformName, appNameLower } = require('./src/config.js') +const { logger } = require('./src/logging.js') const downloadloc = require('./downloadloc') -const getDownloadLocation = downloadloc.getDownloadLocation -const appName = downloadloc.appName() -const appNameLower = appName.toLowerCase() +const { downloadSpecterd, destroyProgressbar } = require('./src/download.js') +const { startSpecterd, quitSpecterd } = require('./src/specterd.js') +const { getFileHash, versionData, isDev, devFolder, isMac } = require('./src/helpers.js') +const { getAppSettings } = require('./src/config.js') +const { showError, updatingLoaderMsg, initMainWindow, loadUrl, initTray } = require('./src/uiHelpers.js') // Quit again if there is no version-data in dev if (isDev && versionData === undefined) { @@ -39,23 +27,6 @@ ipcMain.handle('showMessageBoxSync', (e, message, buttons) => { dialog.showMessageBoxSync(mainWindow, { message, buttons }) }) -// Logging -const { transports, format, createLogger } = require('winston') -const combinedLog = new transports.File({ filename: specterAppLogPath }) -const winstonOptions = { - exitOnError: false, - format: format.combine( - format.timestamp(), - format.json(), - format.printf((info) => { - return `${info.timestamp} [${info.level}] : ${info.message}` - }) - ), - transports: [new transports.Console({ json: false }), combinedLog], - exceptionHandlers: [combinedLog], -} -const logger = createLogger(winstonOptions) - if (isDev) { logger.info('Running the Electron app in dev mode.') } @@ -75,11 +46,10 @@ if (isMac && isDev) { app.dock.setIcon(dockIcon) } -let appSettings = getAppSettings() let dimensions = { width: 1500, height: 1000 } // Modify the context menu -const contextMenu = require('electron-context-menu') + contextMenu({ menu: (actions) => [ { @@ -101,136 +71,16 @@ contextMenu({ ], }) -// The standard quit item cannot be replaced / modified and it is not triggering the -// before-quit event on MacOS if a child window is open -const dockMenuWithforceQuit = Menu.buildFromTemplate([ - { - label: 'Force Quit during download', - click: () => { - // If the progress bar exists, close it - if (progressBar) { - progressBar.close() - } - // Quit the app - app.quit() - }, - }, -]) - -// Download function with progress bar -let progressBar -const download = (uri, filename, callback) => { - // HEAD request first - request.head(uri, (err, res, body) => { - if (res.statusCode != 404) { - let receivedBytes = 0 - const totalBytes = res.headers['content-length'] - logger.info(`Total size to download: ${totalBytes}`) - progressBar = new ProgressBar({ - indeterminate: false, - abortOnError: true, - text: 'Downloading the Specter binary from GitHub', - detail: - 'This can take several minutes depending on your Internet connection. Specter will start once the download is finished.', - maxValue: totalBytes, - browserWindow: { - parent: mainWindow, - }, - style: { - detail: { - 'margin-bottom': '12px', - }, - bar: { - 'background-color': '#fff', - }, - value: { - 'background-color': '#000', - }, - }, - }) - - // Add Force Quit item during download for MacOS dock - if (isMac) { - app.dock.setMenu(dockMenuWithforceQuit) - } - - progressBar.on('completed', () => { - progressBar.close() - // Remove the Force Quit dock item again for Mac - if (isMac) { - const updatedDockMenu = Menu.buildFromTemplate( - dockMenuWithforceQuit.items.filter((item) => item.label !== 'Force Quit during download') - ) - app.dock.setMenu(updatedDockMenu) - } - }) - - progressBar.on('aborted', () => { - logger.info('Download was aborted before it could finish.') - }) - - // Loggin the download progress - let lastLogTime = 0 - const logInterval = 5000 // log every 5 seconds - progressBar.on('progress', () => { - const currentTime = Date.now() - if (currentTime - lastLogTime >= logInterval) { - lastLogTime = currentTime - logger.info(`Download status: ${((receivedBytes / totalBytes) * 100).toFixed(0)}%`) - } - }) - - // GET request - request(uri) - .on('data', (chunk) => { - receivedBytes += chunk.length - if (progressBar) { - progressBar.value = receivedBytes - } - }) - .pipe(fs.createWriteStream(filename)) - .on('close', callback) - } - // If the download link was not found, call callback (updatingLoaderMsg with error feedback) - else { - logger.info(`Error while trying to download specterd: ${err}`) - callback(true) - } - }) -} - let specterdProcess let automaticWalletImport = false let mainWindow let prefWindow -let tray -let trayMenu // Flag the app was quitted let quitted = false -let webPreferences = { - worldSafeExecuteJavaScript: true, - contextIsolation: true, - preload: path.join(__dirname, 'preload.js'), -} - app.commandLine.appendSwitch('ignore-certificate-errors') -let platformName = '' -switch (process.platform) { - case 'darwin': - platformName = 'osx' - break - case 'win32': - platformName = 'win64' - break - case 'linux': - platformName = 'x86_64-linux-gnu' - break - default: - throw `Unknown platformName ${platformName}` -} logger.info('Using version ' + appSettings.specterdVersion) logger.info('Using platformName ' + platformName) @@ -261,92 +111,42 @@ app.on('login', function (event, webContents, request, authInfo, callback) { trySavedAuth = false }) -function createWindow(specterURL) { - if (!mainWindow) { - initMainWindow() - } - - // Create the browser window. - if (appSettings.tor) { - mainWindow.webContents.session.setProxy({ proxyRules: appSettings.proxyURL }) - } - mainWindow.loadURL(specterURL + '?mode=remote') -} - // This method will be called when Electron has finished // initialization and is ready to create browser windows. // Some APIs can only be used after this event occurs. app.whenReady().then(() => { // Create the tray icon logger.info('Framework ready! Starting tray icon ...') - if (isMac) { - const trayIconPath = nativeTheme.shouldUseDarkColors ? '/assets/menu_icon_dark.png' : '/assets/menu_icon_light.png' - const createTrayIcon = (trayIconPath) => { - let trayIcon = nativeImage.createFromPath(app.getAppPath() + trayIconPath) - // Resize - trayIcon = trayIcon.resize({ width: 22, height: 22 }) - return trayIcon - } - const trayIcon = createTrayIcon(trayIconPath) - tray = new Tray(trayIcon) - - // Change the tray icon if appearance is changed in Mac settings - const updateTrayIcon = () => { - logger.info('Updating tray icon ...') - const trayIconPath = nativeTheme.shouldUseDarkColors ? '/assets/menu_icon_dark.png' : '/assets/menu_icon_light.png' - const newTrayIcon = createTrayIcon(trayIconPath) - tray.setImage(newTrayIcon) - } - nativeTheme.on('updated', updateTrayIcon) - } else { - const trayIcon = nativeImage.createFromPath(app.getAppPath() + '/assets/menu_icon.png') - tray = new Tray(trayIcon) - } - - trayMenu = [ - { label: 'Launching Specter ...', enabled: false }, - { - label: 'Show Specter', - click() { - mainWindow.show() - }, - }, - { - label: 'Settings', - click() { - openPreferences() - }, - }, - { - label: 'Quit', - click() { - quitSpecterd() - app.quit() - }, - }, - ] - tray.setToolTip('Specter') - tray.setContextMenu(Menu.buildFromTemplate(trayMenu)) + initTray(openPreferences, quitSpecterd) dimensions = screen.getPrimaryDisplay().size // create a new `splash`-Window logger.info('Framework Ready! Initializing Main-Window, populating Menu ...') - initMainWindow() + mainWindow = initMainWindow(dimensions) + mainWindow.on('close', function (event) { + if (platformName == 'win64') { + quitSpecterd() + app.quit() + } else { + event.preventDefault() + mainWindow.hide() + } + }) setMainMenu() - mainWindow.loadURL(`file://${__dirname}/splash.html`) + loadUrl(`file://${__dirname}/splash.html`) if (!fs.existsSync(specterdDirPath)) { - logger.info('Creating specterd-binaries folder') + logger.info('Creating specterd-binaries folder:' + specterdDirPath) fs.mkdirSync(specterdDirPath, { recursive: true }) } if (!appSettings.versionInitialized || appSettings.versionInitialized != versionData.version) { logger.info(`Updating ${appSettingsPath} : ${JSON.stringify(appSettings)}`) appSettings.specterdVersion = versionData.version - appSettings.specterdHash = versionData.sha256 + appSettings.specterdHash = versionData.sha256[process.arch] appSettings.versionInitialized = versionData.version fs.writeFileSync(appSettingsPath, JSON.stringify(appSettings)) } @@ -377,294 +177,6 @@ app.whenReady().then(() => { } }) -function initMainWindow() { - // In production we use the icons from the build folder - // Note: On MacOS setting an icon here as no effect - const iconPath = isDev ? path.join(__dirname, 'assets-dev/app_icon.png') : '' - mainWindow = new BrowserWindow({ - width: parseInt(dimensions.width * 0.8), - minWidth: 1120, - height: parseInt(dimensions.height * 0.8), - icon: iconPath, - webPreferences, - }) - - // Ensures that any links with target="_blank" or window.open() will be opened in the user's default browser instead of within the app - mainWindow.webContents.setWindowOpenHandler(({ url }) => { - shell.openExternal(url) - return { action: 'deny' } - }) - - mainWindow.on('close', function (event) { - if (platformName == 'win64') { - quitSpecterd() - app.quit() - } else { - event.preventDefault() - mainWindow.hide() - } - }) - - mainWindow.webContents.on('did-fail-load', function () { - mainWindow.loadURL(`file://${__dirname}/splash.html`) - updatingLoaderMsg( - `Failed to load: ${appSettings.specterURL}
Please make sure the URL is entered correctly in the settings and try again...` - ) - }) -} - -function downloadSpecterd(specterdPath) { - updatingLoaderMsg(`Starting download`) - updateSpecterdStatus(`Downloading the ${appName} binary...`) - // Some logging - logger.info('Using version ' + appSettings.specterdVersion) - logger.info('Using platformName ' + platformName) - download_location = getDownloadLocation(appSettings.specterdVersion, platformName) - logger.info('Downloading from ' + download_location) - download(download_location, specterdPath + '.zip', function (errored) { - if (errored == true) { - updatingLoaderMsg( - `Downloading the ${appNameLower} binary from GitHub failed, could not reach the server or the file wasn't found.` - ) - updateSpecterdStatus(`Downloading ${appNameLower}d failed...`) - return - } - updatingLoaderMsg('Download completed. Unpacking files...') - logger.info('Extracting ' + specterdPath) - - extract(specterdPath + '.zip', { dir: specterdPath + '-dir' }).then(function () { - let extraPath = '' - switch (process.platform) { - case 'darwin': - extraPath = appNameLower + 'd' - break - case 'win32': - extraPath = appNameLower + 'd.exe' - break - case 'linux': - extraPath = appNameLower + 'd' - } - var oldPath = specterdPath + `-dir/${extraPath}` - var newPath = specterdPath + (platformName == 'win64' ? '.exe' : '') - - fs.renameSync(oldPath, newPath) - fs.unlinkSync(specterdPath + '.zip') - fs.rmdirSync(specterdPath + '-dir', { recursive: true }) - getFileHash(specterdPath + (platformName == 'win64' ? '.exe' : ''), function (specterdHash) { - if (appSettings.specterdHash.toLowerCase() === specterdHash || appSettings.specterdHash == '') { - startSpecterd(specterdPath) - } else { - updatingLoaderMsg('Specterd version could not be validated.') - logger.error(`hash of downloaded file: ${specterdHash}`) - logger.error(`Expected hash: ${appSettings.specterdHash}`) - updateSpecterdStatus('Failed to launch specterd...') - } - }) - }) - }) -} - -function updateSpecterdStatus(status) { - trayMenu[0] = { label: status, enabled: false } - tray.setContextMenu(Menu.buildFromTemplate(trayMenu)) -} - -function updatingLoaderMsg(msg, showSpinner = false) { - if (mainWindow) { - let code = ` - var launchText = document.getElementById('launch-text'); - if (launchText) { - launchText.innerHTML = '${msg}'; - } - var spinnerElement = document.getElementById('spinner'); - if (spinnerElement) { - if (${showSpinner} === true) { - spinnerElement.classList.remove('hidden') - } - else { - spinnerElement.classList.add('hidden') - } - } - ` - mainWindow.webContents.executeJavaScript(code) - } - logger.info('Updated LoaderMsg: ' + msg) -} - -function checkSpecterd(logs, specterdStarted) { - // There doesn't seem to be another more straightforward way to check whether specterd is running: https://github.com/nodejs/help/issues/1191 - // Setting a timeout to avoid waiting for specterd endlessly - const timeout = 180000 // 3 minutes - const now = Date.now() - const timeElapsed = now - specterdStarted - if (timeElapsed > timeout) { - return 'timeout' - } - if (logs.toString().includes('Serving Flask app')) { - return 'running' - } else { - return 'not running' - } -} - -let specterIsRunning = false -function startSpecterd(specterdPath) { - if (platformName == 'win64') { - specterdPath += '.exe' - } - let appSettings = getAppSettings() - let hwiBridgeMode = appSettings.mode == 'hwibridge' - updatingLoaderMsg('Launching Specter ...', (showSpinner = true)) - updateSpecterdStatus('Launching Specter ...') - let specterdArgs = ['server'] - specterdArgs.push('--no-filelog') - if (hwiBridgeMode) specterdArgs.push('--hwibridge') - if (appSettings.specterdCLIArgs != '') { - // User has inputed cli arguments in the UI - let specterdExtraArgs = appSettings.specterdCLIArgs.split(' ') - specterdExtraArgs.forEach((arg) => { - // Ensures that whitespaces are not used as cli arguments - if (arg != '') { - specterdArgs.push(arg) - } - }) - } - // locale fix (copying from nodejs-env + adding locales) - const options = { - env: { ...process.env }, - } - options.env['LC_ALL'] = 'en_US.utf-8' - options.env['LANG'] = 'en_US.utf-8' - options.env['SPECTER_LOGFORMAT'] = 'SPECTERD: %(levelname)s in %(module)s: %(message)s' - specterdProcess = spawn(specterdPath, specterdArgs, options) - const specterdStarted = Date.now() - - // We are checking for both, stdout and stderr, to be on the save side. - specterdProcess.stdout.on('data', (data) => { - logger.info('stdout-' + data.toString()) - let serverdStatus = checkSpecterd(data, specterdStarted) - // We don't want to check the logs forever, just until specterd is up and running - if (!specterIsRunning) { - if (serverdStatus === 'running') { - logger.info(`Specter server seems to run ...`) - updateSpecterdStatus('Specter is running') - specterIsRunning = true - if (mainWindow) { - if (automaticWalletImport === true) { - logger.info('Performing automatic wallet import ...') - updatingLoaderMsg('Launching wallet importer. This will only work with a node connection.', (showSpinner = true)) - setTimeout(() => { - importWallet(walletDataFromUrl) - }, 3000) - } else { - logger.info('Normal startup of Specter.') - createWindow(appSettings.specterURL) - } - } - } else if (serverdStatus === 'timeout') { - showError('Specter does not seem to start. Check the logs in the menu for more details.') - updateSpecterdStatus('Specter does not start') - logger.error('Startup timeout for specterd exceeded') - } else { - updatingLoaderMsg('Still waiting for Specter to start ...') - updateSpecterdStatus('Specter is starting') - } - } - }) - - specterdProcess.stderr.on('data', (data) => { - logger.info('stderr-' + data.toString()) - let serverdStatus = checkSpecterd(data, specterdStarted) - if (!specterIsRunning) { - if (serverdStatus === 'running') { - logger.info(`Specter server seems to run ...`) - updateSpecterdStatus('Specter is running') - specterIsRunning = true - if (mainWindow) { - logger.info('... creating Electron window for it.') - createWindow(appSettings.specterURL) - } - } else if (serverdStatus === 'timeout') { - showError('Specter does not seem to start. Check the logs in the menu for more details.') - updateSpecterdStatus('Specter does not start') - logger.error('Startup timeout for specterd exceeded') - } else { - updatingLoaderMsg('Still waiting for Specter to start ...') - updateSpecterdStatus('Specter is starting') - } - } - }) - - specterdProcess.on('exit', (code) => { - logger.error(`specterd exited with code ${code}`) - showError(`Specter exited with exit code ${code}. Check the logs in the menu for more details.`) - }) - - specterdProcess.on('error', (err) => { - logger.error(`Error starting Specter server: ${err}`) - showError(`Specter failed to start, due to ${err.message}. Check the logs in the menu for more details.`) - }) - - app.on('activate', function () { - // On macOS it's common to re-create a window in the app when the - // dock icon is clicked and there are no other windows open. - if (BrowserWindow.getAllWindows().length === 0) createWindow(appSettings.specterURL) - }) - // since these are streams, you can pipe them elsewhere - specterdProcess.on('close', (code) => { - updateSpecterdStatus('Specter stopped...') - logger.info(`child process exited with code ${code}`) - }) -} - -let walletDataFromUrl -// Checking whether the app was opened via a Specter URL and determine whether to perform a specific startup action -app.on('open-url', (_, url) => { - logger.info('The app was opened via URL, checking the URL to decide whether to do any automatic actions ...') - // Parse the URL to extract the query parameters - const specterUrl = new URL(url) - const searchParams = specterUrl.searchParams - // Get the query parameter values - const action = searchParams.get('action') - const data = searchParams.get('data') - if (action === 'importWallet' && data !== '') { - logger.info('Automatic wallet import identified in the URL, setting automaticWalletImport to true.') - automaticWalletImport = true - walletDataFromUrl = data - // Directly import if the app and specterd is already running - if (specterIsRunning) { - logger.info('Performing automatic wallet import ...') - mainWindow.loadURL(`file://${__dirname}/splash.html`) - updatingLoaderMsg('Launching wallet importer. This will only work with a node connection.', (showSpinner = true)) - setTimeout(() => { - importWallet(walletDataFromUrl) - }, 3000) - } - } -}) - -// Automatically import the wallet json string, bring user to the final import wallet screen. -// Only proceed with the import if the importFromWalletSoftwareBtn can be found. -// If it is not, users are redirected by specterd to the configure connection screen. -function importWallet(walletData) { - mainWindow.loadURL(appSettings.specterURL + '/wallets/new_wallet/') - if (mainWindow) { - let code = ` - const importFromWalletSoftwareBtn = document.getElementById('import-from-wallet-software-btn') - if (importFromWalletSoftwareBtn) { - importFromWalletSoftwareBtn.click() - const walletDataTextArea = document.getElementById('txt') - if (walletDataTextArea) { - walletDataTextArea.value = \`${walletData}\` - } - const continueBtn = document.getElementById('continue-with-wallet-import-btn'); - continueBtn.click() - } - ` - mainWindow.webContents.executeJavaScript(code) - } -} - app.on('window-all-closed', function () { if (platformName == 'win64') { quitSpecterd() @@ -679,13 +191,7 @@ app.on('before-quit', (event) => { quitted = true quitSpecterd() if (mainWindow && !mainWindow.isDestroyed()) { - if (progressBar) { - // You can only destroy the progress bar if it hadn't been closed before - if (progressBar.browserWindow) { - progressBar.destroy() - } - progressBar = null - } + destroyProgressbar() mainWindow.destroy() mainWindow = null prefWindow = null @@ -715,21 +221,6 @@ ipcMain.on('request-mainprocess-action', (event, arg) => { } }) -function quitSpecterd() { - if (specterdProcess) { - try { - if (platformName == 'win64') { - exec('taskkill /F /T /PID ' + specterdProcess.pid) - exec('taskkill /IM specterd.exe ') - process.kill(-specterdProcess.pid) - } - specterdProcess.kill('SIGINT') - } catch (e) { - logger.info('Specterd quit warning: ' + e) - } - } -} - function setMainMenu() { const menu = defaultMenu(app, shell) @@ -808,21 +299,17 @@ function openErrorLog() { createNewWindow('error_logs.html', width, height).show() } -function showError(error) { - updatingLoaderMsg('Specter encountered an error:
' + error.toString()) -} - process.on('unhandledRejection', (error) => { showError(error) - logger.error(error.toString(), error.name) + logger.error(error.stack) }) process.on('uncaughtException', (error) => { showError(error) // I would love to rethrow the error here as this would create a stacktrace in the logs // but this will terminate the whole process even though i've set - // exitOnError: false in the wistonOptions above. + // exitOnError: false in the winstonOptions above. // Unacceptable for the folks which can't use a commandline, clicking an icon //throw(error) - logger.error(error.toString()) + logger.error(error.stack) }) diff --git a/pyinstaller/electron/package-lock.json b/pyinstaller/electron/package-lock.json index 65defe8ed7..7c9a800a17 100644 --- a/pyinstaller/electron/package-lock.json +++ b/pyinstaller/electron/package-lock.json @@ -1,26 +1,34 @@ { "name": "Specter", - "version": "v0.0.0", + "version": "v2.0.4-pre9", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "Specter", - "version": "v0.0.0", + "version": "v2.0.4-pre9", "hasInstallScript": true, "license": "MIT", "dependencies": { - "electron-context-menu": "^2.3.0", + "electron-context-menu": "^3.6.1", "electron-default-menu": "^1.0.2", - "electron-progressbar": "^2.0.1", + "electron-progressbar": "^2.2.1", "extract-zip": "^2.0.1", "read-last-lines": "^1.8.0", "request": "^2.88.2", - "winston": "^3.3.3" + "winston": "^3.13.0" }, "devDependencies": { - "electron": "^22.1.0", - "electron-builder": "^23.3.1" + "electron": "^30.0.6", + "electron-builder": "^24.13.3" + } + }, + "node_modules/@colors/colors": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz", + "integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==", + "engines": { + "node": ">=0.1.90" } }, "node_modules/@dabh/diagnostics": { @@ -50,6 +58,45 @@ "url": "https://opencollective.com/webpack" } }, + "node_modules/@electron/asar": { + "version": "3.2.10", + "resolved": "https://registry.npmjs.org/@electron/asar/-/asar-3.2.10.tgz", + "integrity": "sha512-mvBSwIBUeiRscrCeJE1LwctAriBj65eUDm0Pc11iE5gRwzkmsdbS7FnZ1XUWjpSeQWL1L5g12Fc/SchPM9DUOw==", + "dev": true, + "dependencies": { + "commander": "^5.0.0", + "glob": "^7.1.6", + "minimatch": "^3.0.4" + }, + "bin": { + "asar": "bin/asar.js" + }, + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/@electron/asar/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@electron/asar/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/@electron/get": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/@electron/get/-/get-2.0.2.tgz", @@ -255,16 +302,134 @@ "semver": "bin/semver.js" } }, + "node_modules/@electron/notarize": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@electron/notarize/-/notarize-2.2.1.tgz", + "integrity": "sha512-aL+bFMIkpR0cmmj5Zgy0LMKEpgy43/hw5zadEArgmAMWWlKc5buwFvFT9G/o/YJkvXAJm5q3iuTuLaiaXW39sg==", + "dev": true, + "dependencies": { + "debug": "^4.1.1", + "fs-extra": "^9.0.1", + "promise-retry": "^2.0.1" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@electron/notarize/node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dev": true, + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@electron/notarize/node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/@electron/notarize/node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@electron/osx-sign": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@electron/osx-sign/-/osx-sign-1.0.5.tgz", + "integrity": "sha512-k9ZzUQtamSoweGQDV2jILiRIHUu7lYlJ3c6IEmjv1hC17rclE+eb9U+f6UFlOOETo0JzY1HNlXy4YOlCvl+Lww==", + "dev": true, + "dependencies": { + "compare-version": "^0.1.2", + "debug": "^4.3.4", + "fs-extra": "^10.0.0", + "isbinaryfile": "^4.0.8", + "minimist": "^1.2.6", + "plist": "^3.0.5" + }, + "bin": { + "electron-osx-flat": "bin/electron-osx-flat.js", + "electron-osx-sign": "bin/electron-osx-sign.js" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@electron/osx-sign/node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@electron/osx-sign/node_modules/isbinaryfile": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-4.0.10.tgz", + "integrity": "sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw==", + "dev": true, + "engines": { + "node": ">= 8.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/gjtorikian/" + } + }, + "node_modules/@electron/osx-sign/node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/@electron/osx-sign/node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "engines": { + "node": ">= 10.0.0" + } + }, "node_modules/@electron/universal": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@electron/universal/-/universal-1.2.1.tgz", - "integrity": "sha512-7323HyMh7KBAl/nPDppdLsC87G6RwRU02dy5FPeGB1eS7rUePh55+WNWiDPLhFQqqVPHzh77M69uhmoT8XnwMQ==", + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/@electron/universal/-/universal-1.5.1.tgz", + "integrity": "sha512-kbgXxyEauPJiQQUNG2VgUeyfQNFk6hBF11ISN2PNI6agUgPl55pv4eQmaqHzTAzchBvqZ2tQuRVaPStGf0mxGw==", "dev": true, "dependencies": { + "@electron/asar": "^3.2.1", "@malept/cross-spawn-promise": "^1.1.0", - "asar": "^3.1.0", "debug": "^4.3.1", - "dir-compare": "^2.4.0", + "dir-compare": "^3.0.0", "fs-extra": "^9.0.1", "minimatch": "^3.0.4", "plist": "^3.0.4" @@ -273,6 +438,16 @@ "node": ">=8.6" } }, + "node_modules/@electron/universal/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, "node_modules/@electron/universal/node_modules/fs-extra": { "version": "9.1.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", @@ -300,15 +475,123 @@ "graceful-fs": "^4.1.6" } }, + "node_modules/@electron/universal/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/@electron/universal/node_modules/universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", "dev": true, "engines": { "node": ">= 10.0.0" } }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, "node_modules/@malept/cross-spawn-promise": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@malept/cross-spawn-promise/-/cross-spawn-promise-1.1.1.tgz", @@ -374,14 +657,24 @@ } }, "node_modules/@malept/flatpak-bundler/node_modules/universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", "dev": true, "engines": { "node": ">= 10.0.0" } }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "optional": true, + "engines": { + "node": ">=14" + } + }, "node_modules/@tootallnate/once": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", @@ -404,9 +697,9 @@ } }, "node_modules/@types/debug": { - "version": "4.1.7", - "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.7.tgz", - "integrity": "sha512-9AonUzyTjXXhEOa0DnqpzZi6VHlqKMswga9EXjpXnnqxwLtdvPPtlO8evrI5D9S6asFRCQ6v+wpiUKbw+vKqyg==", + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", + "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", "dev": true, "dependencies": { "@types/ms": "*" @@ -421,17 +714,6 @@ "@types/node": "*" } }, - "node_modules/@types/glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==", - "dev": true, - "optional": true, - "dependencies": { - "@types/minimatch": "*", - "@types/node": "*" - } - }, "node_modules/@types/http-cache-semantics": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz", @@ -447,29 +729,25 @@ "@types/node": "*" } }, - "node_modules/@types/minimatch": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-5.1.2.tgz", - "integrity": "sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==", - "dev": true, - "optional": true - }, "node_modules/@types/ms": { - "version": "0.7.31", - "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.31.tgz", - "integrity": "sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==", + "version": "0.7.34", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.34.tgz", + "integrity": "sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==", "dev": true }, "node_modules/@types/node": { - "version": "16.18.11", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.11.tgz", - "integrity": "sha512-3oJbGBUWuS6ahSnEq1eN2XrCyf4YsWI8OyCvo7c64zQJNplk3mO84t53o8lfTk+2ji59g5ycfc6qQ3fdHliHuA==", - "devOptional": true + "version": "20.12.12", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.12.tgz", + "integrity": "sha512-eWLDGF/FOSPtAvEqeRAQ4C8LSA7M1I7i0ky1I8U7kD1J5ITyW3AsRhQrKVoWf5pFKZ2kILsEGJhsI9r93PYnOw==", + "devOptional": true, + "dependencies": { + "undici-types": "~5.26.4" + } }, "node_modules/@types/plist": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@types/plist/-/plist-3.0.2.tgz", - "integrity": "sha512-ULqvZNGMv0zRFvqn8/4LSPtnmN4MfhlPNtJCTpKuIIxGVGZ2rYWzFXrvEBoh9CVyqSE7D6YFRJ1hydLHI6kbWw==", + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@types/plist/-/plist-3.0.5.tgz", + "integrity": "sha512-E6OCaRmAe4WDmWNsL/9RMqdkkzDCY1etutkflWk4c+AcjDU07Pcz1fQwTX0TQz+Pxqn9i4L1TU3UFpjnrcDgxA==", "dev": true, "optional": true, "dependencies": { @@ -486,28 +764,18 @@ "@types/node": "*" } }, + "node_modules/@types/triple-beam": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.5.tgz", + "integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==" + }, "node_modules/@types/verror": { - "version": "1.10.6", - "resolved": "https://registry.npmjs.org/@types/verror/-/verror-1.10.6.tgz", - "integrity": "sha512-NNm+gdePAX1VGvPcGZCDKQZKYSiAWigKhKaz5KF94hG6f2s8de9Ow5+7AbXoeKxL8gavZfk4UquSAygOF2duEQ==", + "version": "1.10.10", + "resolved": "https://registry.npmjs.org/@types/verror/-/verror-1.10.10.tgz", + "integrity": "sha512-l4MM0Jppn18hb9xmM6wwD1uTdShpf9Pn80aXTStnK1C94gtPvJcV2FrDmbOQUAQfJ1cKZHktkQUDwEqaAKXMMg==", "dev": true, "optional": true }, - "node_modules/@types/yargs": { - "version": "17.0.22", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.22.tgz", - "integrity": "sha512-pet5WJ9U8yPVRhkwuEIp5ktAeAqRZOq4UdAyWLWzxbtpyXnzbtLdKiXAjJzi/KLmPGS9wk86lUFWZFN6sISo4g==", - "dev": true, - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/@types/yargs-parser": { - "version": "21.0.0", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz", - "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==", - "dev": true - }, "node_modules/@types/yauzl": { "version": "2.9.1", "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.9.1.tgz", @@ -517,10 +785,19 @@ "@types/node": "*" } }, + "node_modules/@xmldom/xmldom": { + "version": "0.8.10", + "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.10.tgz", + "integrity": "sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw==", + "dev": true, + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/7zip-bin": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/7zip-bin/-/7zip-bin-5.1.1.tgz", - "integrity": "sha512-sAP4LldeWNz0lNzmTird3uWfFDWWTeg6V/MsmyyLR9X1idwKBWIgt/ZvinqQldJm3LecKEs1emkbquO6PCiLVQ==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/7zip-bin/-/7zip-bin-5.2.0.tgz", + "integrity": "sha512-ukTPVhqG4jNzMro2qA9HSCSSVJN3aN7tlb+hfqYCt3ER0yWroeA2VR38MNrOHLQ/cVj+DaIMad0kFCtWWowh/A==", "dev": true }, "node_modules/agent-base": { @@ -586,40 +863,45 @@ "dev": true }, "node_modules/app-builder-lib": { - "version": "23.6.0", - "resolved": "https://registry.npmjs.org/app-builder-lib/-/app-builder-lib-23.6.0.tgz", - "integrity": "sha512-dQYDuqm/rmy8GSCE6Xl/3ShJg6Ab4bZJMT8KaTKGzT436gl1DN4REP3FCWfXoh75qGTJ+u+WsdnnpO9Jl8nyMA==", + "version": "24.13.3", + "resolved": "https://registry.npmjs.org/app-builder-lib/-/app-builder-lib-24.13.3.tgz", + "integrity": "sha512-FAzX6IBit2POXYGnTCT8YHFO/lr5AapAII6zzhQO3Rw4cEDOgK+t1xhLc5tNcKlicTHlo9zxIwnYCX9X2DLkig==", "dev": true, "dependencies": { "@develar/schema-utils": "~2.6.5", - "@electron/universal": "1.2.1", + "@electron/notarize": "2.2.1", + "@electron/osx-sign": "1.0.5", + "@electron/universal": "1.5.1", "@malept/flatpak-bundler": "^0.4.0", - "7zip-bin": "~5.1.1", + "@types/fs-extra": "9.0.13", "async-exit-hook": "^2.0.1", "bluebird-lst": "^1.0.9", - "builder-util": "23.6.0", - "builder-util-runtime": "9.1.1", + "builder-util": "24.13.1", + "builder-util-runtime": "9.2.4", "chromium-pickle-js": "^0.2.0", "debug": "^4.3.4", - "ejs": "^3.1.7", - "electron-osx-sign": "^0.6.0", - "electron-publish": "23.6.0", + "ejs": "^3.1.8", + "electron-publish": "24.13.1", "form-data": "^4.0.0", "fs-extra": "^10.1.0", "hosted-git-info": "^4.1.0", "is-ci": "^3.0.0", - "isbinaryfile": "^4.0.10", + "isbinaryfile": "^5.0.0", "js-yaml": "^4.1.0", "lazy-val": "^1.0.5", - "minimatch": "^3.1.2", - "read-config-file": "6.2.0", + "minimatch": "^5.1.1", + "read-config-file": "6.3.2", "sanitize-filename": "^1.6.3", - "semver": "^7.3.7", - "tar": "^6.1.11", + "semver": "^7.3.8", + "tar": "^6.1.12", "temp-file": "^3.4.0" }, "engines": { "node": ">=14.0.0" + }, + "peerDependencies": { + "dmg-builder": "24.13.3", + "electron-builder-squirrel-windows": "24.13.3" } }, "node_modules/app-builder-lib/node_modules/form-data": { @@ -656,19 +938,75 @@ "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", "dev": true, "dependencies": { - "universalify": "^2.0.0" + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/app-builder-lib/node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/archiver": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/archiver/-/archiver-5.3.2.tgz", + "integrity": "sha512-+25nxyyznAXF7Nef3y0EbBeqmGZgeN/BxHX29Rs39djAfaFalmQ89SE6CWyDCHzGL0yt/ycBtNOmGTW0FyGWNw==", + "dev": true, + "peer": true, + "dependencies": { + "archiver-utils": "^2.1.0", + "async": "^3.2.4", + "buffer-crc32": "^0.2.1", + "readable-stream": "^3.6.0", + "readdir-glob": "^1.1.2", + "tar-stream": "^2.2.0", + "zip-stream": "^4.1.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/archiver-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-2.1.0.tgz", + "integrity": "sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw==", + "dev": true, + "peer": true, + "dependencies": { + "glob": "^7.1.4", + "graceful-fs": "^4.2.0", + "lazystream": "^1.0.0", + "lodash.defaults": "^4.2.0", + "lodash.difference": "^4.5.0", + "lodash.flatten": "^4.4.0", + "lodash.isplainobject": "^4.0.6", + "lodash.union": "^4.6.0", + "normalize-path": "^3.0.0", + "readable-stream": "^2.0.0" }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" + "engines": { + "node": ">= 6" } }, - "node_modules/app-builder-lib/node_modules/universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "node_modules/archiver/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", "dev": true, + "peer": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, "engines": { - "node": ">= 10.0.0" + "node": ">= 6" } }, "node_modules/argparse": { @@ -677,28 +1015,6 @@ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true }, - "node_modules/asar": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/asar/-/asar-3.2.0.tgz", - "integrity": "sha512-COdw2ZQvKdFGFxXwX3oYh2/sOsJWJegrdJCGxnN4MZ7IULgRBp9P6665aqj9z1v9VwP4oP1hRBojRDQ//IGgAg==", - "deprecated": "Please use @electron/asar moving forward. There is no API change, just a package name change", - "dev": true, - "dependencies": { - "chromium-pickle-js": "^0.2.0", - "commander": "^5.0.0", - "glob": "^7.1.6", - "minimatch": "^3.0.4" - }, - "bin": { - "asar": "bin/asar.js" - }, - "engines": { - "node": ">=10.12.0" - }, - "optionalDependencies": { - "@types/glob": "^7.1.1" - } - }, "node_modules/asn1": { "version": "0.2.4", "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", @@ -724,9 +1040,9 @@ } }, "node_modules/async": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.3.tgz", - "integrity": "sha512-spZRyzKL5l5BZQrr/6m/SqFdBN0q3OCI0f9rjfBzCMBIP4p75P620rR3gTmaksNOhmzgdxcaxdNfMy6anrbM0g==" + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz", + "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==" }, "node_modules/async-exit-hook": { "version": "2.0.1", @@ -798,6 +1114,33 @@ "tweetnacl": "^0.14.3" } }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dev": true, + "peer": true, + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/bl/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "peer": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/bluebird": { "version": "3.7.2", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", @@ -821,13 +1164,12 @@ "optional": true }, "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "dev": true, "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "balanced-match": "^1.0.0" } }, "node_modules/buffer": { @@ -849,28 +1191,11 @@ "url": "https://feross.org/support" } ], - "optional": true, "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.1.13" } }, - "node_modules/buffer-alloc": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz", - "integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==", - "dev": true, - "dependencies": { - "buffer-alloc-unsafe": "^1.1.0", - "buffer-fill": "^1.0.0" - } - }, - "node_modules/buffer-alloc-unsafe": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz", - "integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==", - "dev": true - }, "node_modules/buffer-crc32": { "version": "0.2.13", "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", @@ -880,20 +1205,17 @@ } }, "node_modules/buffer-equal": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/buffer-equal/-/buffer-equal-1.0.0.tgz", - "integrity": "sha512-tcBWO2Dl4e7Asr9hTGcpVrCe+F7DubpmqWCTbj4FHLmjqO2hIaC383acQubWtRJhdceqs5uBHs6Es+Sk//RKiQ==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal/-/buffer-equal-1.0.1.tgz", + "integrity": "sha512-QoV3ptgEaQpvVwbXdSO39iqPQTCxSF7A5U99AxbHYqUdCizL/lH2Z0A2y6nbZucxMEOtNyZfG2s6gsVugGpKkg==", "dev": true, "engines": { - "node": ">=0.4.0" + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/buffer-fill": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz", - "integrity": "sha512-T7zexNBwiiaCOGDg9xNX9PBmjrubblRkENuptryuI64URkXDFum9il/JGL8Lm8wYfAXpredVXXZz7eMHilimiQ==", - "dev": true - }, "node_modules/buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", @@ -901,23 +1223,22 @@ "dev": true }, "node_modules/builder-util": { - "version": "23.6.0", - "resolved": "https://registry.npmjs.org/builder-util/-/builder-util-23.6.0.tgz", - "integrity": "sha512-QiQHweYsh8o+U/KNCZFSvISRnvRctb8m/2rB2I1JdByzvNKxPeFLlHFRPQRXab6aYeXc18j9LpsDLJ3sGQmWTQ==", + "version": "24.13.1", + "resolved": "https://registry.npmjs.org/builder-util/-/builder-util-24.13.1.tgz", + "integrity": "sha512-NhbCSIntruNDTOVI9fdXz0dihaqX2YuE1D6zZMrwiErzH4ELZHE6mdiB40wEgZNprDia+FghRFgKoAqMZRRjSA==", "dev": true, "dependencies": { "@types/debug": "^4.1.6", - "@types/fs-extra": "^9.0.11", - "7zip-bin": "~5.1.1", + "7zip-bin": "~5.2.0", "app-builder-bin": "4.0.0", "bluebird-lst": "^1.0.9", - "builder-util-runtime": "9.1.1", - "chalk": "^4.1.1", + "builder-util-runtime": "9.2.4", + "chalk": "^4.1.2", "cross-spawn": "^7.0.3", "debug": "^4.3.4", - "fs-extra": "^10.0.0", + "fs-extra": "^10.1.0", "http-proxy-agent": "^5.0.0", - "https-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.1", "is-ci": "^3.0.0", "js-yaml": "^4.1.0", "source-map-support": "^0.5.19", @@ -926,9 +1247,9 @@ } }, "node_modules/builder-util-runtime": { - "version": "9.1.1", - "resolved": "https://registry.npmjs.org/builder-util-runtime/-/builder-util-runtime-9.1.1.tgz", - "integrity": "sha512-azRhYLEoDvRDR8Dhis4JatELC/jUvYjm4cVSj7n9dauGTOM2eeNn9KS0z6YA6oDsjI1xphjNbY6PZZeHPzzqaw==", + "version": "9.2.4", + "resolved": "https://registry.npmjs.org/builder-util-runtime/-/builder-util-runtime-9.2.4.tgz", + "integrity": "sha512-upp+biKpN/XZMLim7aguUyW8s0FUpDvOtK6sbanMFDAMBzpHDqdhgVYm6zc9HJ6nWo7u2Lxk60i2M6Jd3aiNrA==", "dev": true, "dependencies": { "debug": "^4.3.4", @@ -965,9 +1286,9 @@ } }, "node_modules/builder-util/node_modules/universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", "dev": true, "engines": { "node": ">= 10.0.0" @@ -1019,9 +1340,9 @@ "dev": true }, "node_modules/ci-info": { - "version": "3.7.1", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.7.1.tgz", - "integrity": "sha512-4jYS4MOAaCIStSRwiuxc4B8MYhIe676yO1sYGzARnjXkWpmzZMMYxY6zu8WYWDhSuth5zhrQ1rhNSibyyvv4/w==", + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", + "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", "dev": true, "funding": [ { @@ -1115,14 +1436,6 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" }, - "node_modules/colors": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", - "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", - "engines": { - "node": ">=0.1.90" - } - }, "node_modules/colorspace": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.2.tgz", @@ -1161,12 +1474,99 @@ "node": ">=0.10.0" } }, + "node_modules/compress-commons": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-4.1.2.tgz", + "integrity": "sha512-D3uMHtGc/fcO1Gt1/L7i1e33VOvD4A9hfQLP+6ewd+BvG/gQ84Yh4oftEhAdjSMgBgwGL+jsppT7JYNpo6MHHg==", + "dev": true, + "peer": true, + "dependencies": { + "buffer-crc32": "^0.2.13", + "crc32-stream": "^4.0.2", + "normalize-path": "^3.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/compress-commons/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "peer": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true }, + "node_modules/config-file-ts": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/config-file-ts/-/config-file-ts-0.2.6.tgz", + "integrity": "sha512-6boGVaglwblBgJqGyxm4+xCmEGcWgnWHSWHY5jad58awQhB6gftq0G8HbzU39YqCIYHMLAiL1yjwiZ36m/CL8w==", + "dev": true, + "dependencies": { + "glob": "^10.3.10", + "typescript": "^5.3.3" + } + }, + "node_modules/config-file-ts/node_modules/glob": { + "version": "10.3.16", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.16.tgz", + "integrity": "sha512-JDKXl1DiuuHJ6fVS2FXjownaavciiHNUU4mOvV/B793RLh05vZL1rcPnCSaOgv1hDT6RDlY7AB7ZUvFYAtPgAw==", + "dev": true, + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.1", + "minipass": "^7.0.4", + "path-scurry": "^1.11.0" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/config-file-ts/node_modules/minimatch": { + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", + "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/config-file-ts/node_modules/minipass": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.1.tgz", + "integrity": "sha512-UZ7eQ+h8ywIRAW1hIEl2AqdwzJucU/Kp59+8kkZeSvafXhZjul247BvIJjEVFVeON6d7lM46XX1HXCduKAS8VA==", + "dev": true, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, "node_modules/core-util-is": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", @@ -1182,6 +1582,48 @@ "buffer": "^5.1.0" } }, + "node_modules/crc-32": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", + "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", + "dev": true, + "peer": true, + "bin": { + "crc32": "bin/crc32.njs" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/crc32-stream": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-4.0.3.tgz", + "integrity": "sha512-NT7w2JVU7DFroFdYkeq8cywxrgjPHWkdX1wjpRQXPX5Asews3tA+Ght6lddQO5Mkumffp3X7GEqku3epj2toIw==", + "dev": true, + "peer": true, + "dependencies": { + "crc-32": "^1.2.0", + "readable-stream": "^3.4.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/crc32-stream/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "peer": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -1256,45 +1698,29 @@ "optional": true }, "node_modules/dir-compare": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/dir-compare/-/dir-compare-2.4.0.tgz", - "integrity": "sha512-l9hmu8x/rjVC9Z2zmGzkhOEowZvW7pmYws5CWHutg8u1JgvsKWMx7Q/UODeu4djLZ4FgW5besw5yvMQnBHzuCA==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/dir-compare/-/dir-compare-3.3.0.tgz", + "integrity": "sha512-J7/et3WlGUCxjdnD3HAAzQ6nsnc0WL6DD7WcwJb7c39iH1+AWfg+9OqzJNaI6PkBwBvm1mhZNL9iY/nRiZXlPg==", "dev": true, "dependencies": { - "buffer-equal": "1.0.0", - "colors": "1.0.3", - "commander": "2.9.0", - "minimatch": "3.0.4" - }, - "bin": { - "dircompare": "src/cli/dircompare.js" - } - }, - "node_modules/dir-compare/node_modules/colors": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz", - "integrity": "sha512-pFGrxThWcWQ2MsAz6RtgeWe4NK2kUE1WfsrvvlctdII745EW9I0yflqhe7++M5LEc7bV2c/9/5zc8sFcpL0Drw==", - "dev": true, - "engines": { - "node": ">=0.1.90" + "buffer-equal": "^1.0.0", + "minimatch": "^3.0.4" } }, - "node_modules/dir-compare/node_modules/commander": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.9.0.tgz", - "integrity": "sha512-bmkUukX8wAOjHdN26xj5c4ctEV22TQ7dQYhSmuckKhToXrkUn0iIaolHdIxYYqD55nhpSPA9zPQ1yP57GdXP2A==", + "node_modules/dir-compare/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, "dependencies": { - "graceful-readlink": ">= 1.0.0" - }, - "engines": { - "node": ">= 0.6.x" + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, "node_modules/dir-compare/node_modules/minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, "dependencies": { "brace-expansion": "^1.1.7" @@ -1304,15 +1730,15 @@ } }, "node_modules/dmg-builder": { - "version": "23.6.0", - "resolved": "https://registry.npmjs.org/dmg-builder/-/dmg-builder-23.6.0.tgz", - "integrity": "sha512-jFZvY1JohyHarIAlTbfQOk+HnceGjjAdFjVn3n8xlDWKsYNqbO4muca6qXEZTfGXeQMG7TYim6CeS5XKSfSsGA==", + "version": "24.13.3", + "resolved": "https://registry.npmjs.org/dmg-builder/-/dmg-builder-24.13.3.tgz", + "integrity": "sha512-rcJUkMfnJpfCboZoOOPf4L29TRtEieHNOeAbYPWPxlaBw/Z1RKrRA86dOI9rwaI4tQSc/RD82zTNHprfUHXsoQ==", "dev": true, "dependencies": { - "app-builder-lib": "23.6.0", - "builder-util": "23.6.0", - "builder-util-runtime": "9.1.1", - "fs-extra": "^10.0.0", + "app-builder-lib": "24.13.3", + "builder-util": "24.13.1", + "builder-util-runtime": "9.2.4", + "fs-extra": "^10.1.0", "iconv-lite": "^0.6.2", "js-yaml": "^4.1.0" }, @@ -1347,9 +1773,9 @@ } }, "node_modules/dmg-builder/node_modules/universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", "dev": true, "engines": { "node": ">= 10.0.0" @@ -1396,6 +1822,12 @@ "integrity": "sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==", "dev": true }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true + }, "node_modules/ecc-jsbn": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", @@ -1406,9 +1838,9 @@ } }, "node_modules/ejs": { - "version": "3.1.8", - "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.8.tgz", - "integrity": "sha512-/sXZeMlhS0ArkfX2Aw780gJzXSMPnKjtspYZv+f3NiKLlubezAHDU5+9xz6gd3/NhG3txQCo6xlglmTS+oTGEQ==", + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", + "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", "dev": true, "dependencies": { "jake": "^10.8.5" @@ -1421,14 +1853,14 @@ } }, "node_modules/electron": { - "version": "22.1.0", - "resolved": "https://registry.npmjs.org/electron/-/electron-22.1.0.tgz", - "integrity": "sha512-wz5s4N6V7zyKm4YQmXJImFoxO1Doai32ShYm0FzOLPBMwLMdQBV+REY+j1opRx0KJ9xJEIdjYgcA8OSw6vx3pA==", + "version": "30.0.6", + "resolved": "https://registry.npmjs.org/electron/-/electron-30.0.6.tgz", + "integrity": "sha512-PkhEPFdpYcTzjAO3gMHZ+map7g2+xCrMDedo/L1i0ir2BRXvAB93IkTJX497U6Srb/09r2cFt+k20VPNVCdw3Q==", "dev": true, "hasInstallScript": true, "dependencies": { "@electron/get": "^2.0.0", - "@types/node": "^16.11.26", + "@types/node": "^20.9.0", "extract-zip": "^2.0.1" }, "bin": { @@ -1439,24 +1871,22 @@ } }, "node_modules/electron-builder": { - "version": "23.6.0", - "resolved": "https://registry.npmjs.org/electron-builder/-/electron-builder-23.6.0.tgz", - "integrity": "sha512-y8D4zO+HXGCNxFBV/JlyhFnoQ0Y0K7/sFH+XwIbj47pqaW8S6PGYQbjoObolKBR1ddQFPt4rwp4CnwMJrW3HAw==", + "version": "24.13.3", + "resolved": "https://registry.npmjs.org/electron-builder/-/electron-builder-24.13.3.tgz", + "integrity": "sha512-yZSgVHft5dNVlo31qmJAe4BVKQfFdwpRw7sFp1iQglDRCDD6r22zfRJuZlhtB5gp9FHUxCMEoWGq10SkCnMAIg==", "dev": true, - "license": "MIT", "dependencies": { - "@types/yargs": "^17.0.1", - "app-builder-lib": "23.6.0", - "builder-util": "23.6.0", - "builder-util-runtime": "9.1.1", - "chalk": "^4.1.1", - "dmg-builder": "23.6.0", - "fs-extra": "^10.0.0", + "app-builder-lib": "24.13.3", + "builder-util": "24.13.1", + "builder-util-runtime": "9.2.4", + "chalk": "^4.1.2", + "dmg-builder": "24.13.3", + "fs-extra": "^10.1.0", "is-ci": "^3.0.0", "lazy-val": "^1.0.5", - "read-config-file": "6.2.0", - "simple-update-notifier": "^1.0.7", - "yargs": "^17.5.1" + "read-config-file": "6.3.2", + "simple-update-notifier": "2.0.0", + "yargs": "^17.6.2" }, "bin": { "electron-builder": "cli.js", @@ -1466,11 +1896,25 @@ "node": ">=14.0.0" } }, - "node_modules/electron-builder/node_modules/fs-extra": { + "node_modules/electron-builder-squirrel-windows": { + "version": "24.13.3", + "resolved": "https://registry.npmjs.org/electron-builder-squirrel-windows/-/electron-builder-squirrel-windows-24.13.3.tgz", + "integrity": "sha512-oHkV0iogWfyK+ah9ZIvMDpei1m9ZRpdXcvde1wTpra2U8AFDNNpqJdnin5z+PM1GbQ5BoaKCWas2HSjtR0HwMg==", + "dev": true, + "peer": true, + "dependencies": { + "app-builder-lib": "24.13.3", + "archiver": "^5.3.1", + "builder-util": "24.13.1", + "fs-extra": "^10.1.0" + } + }, + "node_modules/electron-builder-squirrel-windows/node_modules/fs-extra": { "version": "10.1.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", "dev": true, + "peer": true, "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", @@ -1480,11 +1924,12 @@ "node": ">=12" } }, - "node_modules/electron-builder/node_modules/jsonfile": { + "node_modules/electron-builder-squirrel-windows/node_modules/jsonfile": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", "dev": true, + "peer": true, "dependencies": { "universalify": "^2.0.0" }, @@ -1492,113 +1937,112 @@ "graceful-fs": "^4.1.6" } }, - "node_modules/electron-builder/node_modules/universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "node_modules/electron-builder-squirrel-windows/node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", "dev": true, + "peer": true, "engines": { "node": ">= 10.0.0" } }, - "node_modules/electron-context-menu": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/electron-context-menu/-/electron-context-menu-2.3.0.tgz", - "integrity": "sha512-XYsYkNY+jvX4C5o09qMuZoKL6e9frnQzBFehZSIiKp6zK0u3XYowJYDyK3vDKKZxYsOIGiE/Gbx40jERC03Ctw==", + "node_modules/electron-builder/node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, "dependencies": { - "cli-truncate": "^2.0.0", - "electron-dl": "^3.0.0", - "electron-is-dev": "^1.0.1" + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" } }, - "node_modules/electron-default-menu": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/electron-default-menu/-/electron-default-menu-1.0.2.tgz", - "integrity": "sha512-YAL/UNR3kPG58wOOlmDpTG3i6+bzwhHx6NllIOaLuVrU7uYifeYGGdk5IH2Hap4wVEx2YTA8cqQ2PGSplYwDWQ==" - }, - "node_modules/electron-dl": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/electron-dl/-/electron-dl-3.0.2.tgz", - "integrity": "sha512-pRgE9Jbhoo5z6Vk3qi+vIrfpMDlCp2oB1UeR96SMnsfz073jj0AZGQwp69EdIcEvlUlwBSGyJK8Jt6OB6JLn+g==", + "node_modules/electron-builder/node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, "dependencies": { - "ext-name": "^5.0.0", - "pupa": "^2.0.1", - "unused-filename": "^2.1.0" + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" } }, - "node_modules/electron-is-dev": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/electron-is-dev/-/electron-is-dev-1.2.0.tgz", - "integrity": "sha512-R1oD5gMBPS7PVU8gJwH6CtT0e6VSoD0+SzSnYpNm+dBkcijgA+K7VAMHDfnRq/lkKPZArpzplTW6jfiMYosdzw==" - }, - "node_modules/electron-osx-sign": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/electron-osx-sign/-/electron-osx-sign-0.6.0.tgz", - "integrity": "sha512-+hiIEb2Xxk6eDKJ2FFlpofCnemCbjbT5jz+BKGpVBrRNT3kWTGs4DfNX6IzGwgi33hUcXF+kFs9JW+r6Wc1LRg==", - "deprecated": "Please use @electron/osx-sign moving forward. Be aware the API is slightly different", + "node_modules/electron-builder/node_modules/universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", "dev": true, - "dependencies": { - "bluebird": "^3.5.0", - "compare-version": "^0.1.2", - "debug": "^2.6.8", - "isbinaryfile": "^3.0.2", - "minimist": "^1.2.0", - "plist": "^3.0.1" - }, - "bin": { - "electron-osx-flat": "bin/electron-osx-flat.js", - "electron-osx-sign": "bin/electron-osx-sign.js" - }, "engines": { - "node": ">=4.0.0" + "node": ">= 10.0.0" } }, - "node_modules/electron-osx-sign/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, + "node_modules/electron-context-menu": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/electron-context-menu/-/electron-context-menu-3.6.1.tgz", + "integrity": "sha512-lcpO6tzzKUROeirhzBjdBWNqayEThmdW+2I2s6H6QMrwqTVyT3EK47jW3Nxm60KTxl5/bWfEoIruoUNn57/QkQ==", "dependencies": { - "ms": "2.0.0" + "cli-truncate": "^2.1.0", + "electron-dl": "^3.2.1", + "electron-is-dev": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/electron-osx-sign/node_modules/isbinaryfile": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-3.0.3.tgz", - "integrity": "sha512-8cJBL5tTd2OS0dM4jz07wQd5g0dCCqIhUxPIGtZfa5L6hWlvV5MHTITy/DBAsF+Oe2LS1X3krBUhNwaGUWpWxw==", - "dev": true, + "node_modules/electron-default-menu": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/electron-default-menu/-/electron-default-menu-1.0.2.tgz", + "integrity": "sha512-YAL/UNR3kPG58wOOlmDpTG3i6+bzwhHx6NllIOaLuVrU7uYifeYGGdk5IH2Hap4wVEx2YTA8cqQ2PGSplYwDWQ==" + }, + "node_modules/electron-dl": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/electron-dl/-/electron-dl-3.5.2.tgz", + "integrity": "sha512-i104cl+u8yJ0lhpRAtUWfeGuWuL1PL6TBiw2gLf0MMIBjfgE485Ags2mcySx4uWU9P9uj/vsD3jd7X+w1lzZxw==", "dependencies": { - "buffer-alloc": "^1.2.0" + "ext-name": "^5.0.0", + "pupa": "^2.0.1", + "unused-filename": "^2.1.0" }, "engines": { - "node": ">=0.6.0" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/electron-osx-sign/node_modules/ms": { + "node_modules/electron-is-dev": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true + "resolved": "https://registry.npmjs.org/electron-is-dev/-/electron-is-dev-2.0.0.tgz", + "integrity": "sha512-3X99K852Yoqu9AcW50qz3ibYBWY79/pBhlMCab8ToEWS48R0T9tyxRiQhwylE7zQdXrMnx2JKqUJyMPmt5FBqA==", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, "node_modules/electron-progressbar": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/electron-progressbar/-/electron-progressbar-2.0.1.tgz", - "integrity": "sha512-+N60GX2q+KH5OvZXxwtjMTZB/1AyxriFd95vOnR3sOfNpvz+30LMsM0a9SnEivZE6N8Djy7F3z4TY8pLs8aopw==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/electron-progressbar/-/electron-progressbar-2.2.1.tgz", + "integrity": "sha512-LQ9bxM3Tf5PG/1QngY8ywvht7IKvQ8tEIra8uh3RkLASqN/GYvr4r0uU9qz38r0Zn72UcouYzumKDfLwoI/rsw==", "dependencies": { "extend": "^3.0.1" } }, "node_modules/electron-publish": { - "version": "23.6.0", - "resolved": "https://registry.npmjs.org/electron-publish/-/electron-publish-23.6.0.tgz", - "integrity": "sha512-jPj3y+eIZQJF/+t5SLvsI5eS4mazCbNYqatv5JihbqOstIM13k0d1Z3vAWntvtt13Itl61SO6seicWdioOU5dg==", + "version": "24.13.1", + "resolved": "https://registry.npmjs.org/electron-publish/-/electron-publish-24.13.1.tgz", + "integrity": "sha512-2ZgdEqJ8e9D17Hwp5LEq5mLQPjqU3lv/IALvgp+4W8VeNhryfGhYEQC/PgDPMrnWUp+l60Ou5SJLsu+k4mhQ8A==", "dev": true, "dependencies": { "@types/fs-extra": "^9.0.11", - "builder-util": "23.6.0", - "builder-util-runtime": "9.1.1", - "chalk": "^4.1.1", - "fs-extra": "^10.0.0", + "builder-util": "24.13.1", + "builder-util-runtime": "9.2.4", + "chalk": "^4.1.2", + "fs-extra": "^10.1.0", "lazy-val": "^1.0.5", "mime": "^2.5.2" } @@ -1630,14 +2074,19 @@ } }, "node_modules/electron-publish/node_modules/universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", "dev": true, "engines": { "node": ">= 10.0.0" } }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, "node_modules/enabled": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", @@ -1660,6 +2109,12 @@ "node": ">=6" } }, + "node_modules/err-code": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", + "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==", + "dev": true + }, "node_modules/es6-error": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", @@ -1782,9 +2237,9 @@ } }, "node_modules/fecha": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.1.tgz", - "integrity": "sha512-MMMQ0ludy/nBs1/o0zVOiKTpG7qMbonKUzjJgQFEuvq6INZ1OraKPRAWkBq5vlKLOUMpmNYG1JoN3oDPUQ9m3Q==" + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz", + "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==" }, "node_modules/filelist": { "version": "1.0.4", @@ -1795,32 +2250,27 @@ "minimatch": "^5.0.1" } }, - "node_modules/filelist/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0" - } + "node_modules/fn.name": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", + "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==" }, - "node_modules/filelist/node_modules/minimatch": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "node_modules/foreground-child": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", + "integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==", "dev": true, "dependencies": { - "brace-expansion": "^2.0.1" + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" }, "engines": { - "node": ">=10" + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/fn.name": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", - "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==" - }, "node_modules/forever-agent": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", @@ -1842,6 +2292,13 @@ "node": ">= 0.12" } }, + "node_modules/fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "dev": true, + "peer": true + }, "node_modules/fs-extra": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", @@ -1945,6 +2402,28 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/glob/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/global-agent": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/global-agent/-/global-agent-3.0.0.tgz", @@ -1985,12 +2464,6 @@ "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", "dev": true }, - "node_modules/graceful-readlink": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz", - "integrity": "sha512-8tLu60LgxF6XpdbK8OW3FA+IfTNBn1ZHGHKF4KQbEeSkajYw5PlYJcKluntgegDPTg8UkHjpet1T82vk6TQ68w==", - "dev": true - }, "node_modules/har-schema": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", @@ -2178,8 +2651,7 @@ "type": "consulting", "url": "https://feross.org/support" } - ], - "optional": true + ] }, "node_modules/inflight": { "version": "1.0.6", @@ -2213,10 +2685,18 @@ "is-ci": "bin.js" } }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "engines": { + "node": ">=8" + } + }, "node_modules/is-plain-obj": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", - "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=", + "integrity": "sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==", "engines": { "node": ">=0.10.0" } @@ -2240,15 +2720,17 @@ "node_modules/isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true, + "peer": true }, "node_modules/isbinaryfile": { - "version": "4.0.10", - "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-4.0.10.tgz", - "integrity": "sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw==", + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-5.0.2.tgz", + "integrity": "sha512-GvcjojwonMjWbTkfMpnVHVqXW/wKMYDfEpY94/8zy8HFMOqb/VL6oeONq9v87q4ttVlaTLnGXnJD4B5B1OTGIg==", "dev": true, "engines": { - "node": ">= 8.0.0" + "node": ">= 18.0.0" }, "funding": { "url": "https://github.com/sponsors/gjtorikian/" @@ -2265,16 +2747,34 @@ "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" }, + "node_modules/jackspeak": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.1.2.tgz", + "integrity": "sha512-kWmLKn2tRtfYMF/BakihVVRzBKOxz4gJMiL2Rj91WnAB5TPZumSH99R/Yf1qE1u4uRimvCSJfm6hnxohXeEXjQ==", + "dev": true, + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, "node_modules/jake": { - "version": "10.8.5", - "resolved": "https://registry.npmjs.org/jake/-/jake-10.8.5.tgz", - "integrity": "sha512-sVpxYeuAhWt0OTWITwT98oyV0GsXyMlXCF+3L1SuafBVUIr/uILGRB+NqwkzhgXKvoJpDIpQvqkUALgdmQsQxw==", + "version": "10.9.1", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.1.tgz", + "integrity": "sha512-61btcOHNnLnsOdtLgA5efqQWjnSi/vow5HbI7HMdKKWqvrKR1bLK3BPlJn9gcSaP2ewuamUSMB5XEy76KUIS2w==", "dev": true, "dependencies": { "async": "^3.2.3", "chalk": "^4.0.2", - "filelist": "^1.0.1", - "minimatch": "^3.0.4" + "filelist": "^1.0.4", + "minimatch": "^3.1.2" }, "bin": { "jake": "bin/cli.js" @@ -2283,6 +2783,28 @@ "node": ">=10" } }, + "node_modules/jake/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/jake/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", @@ -2361,22 +2883,74 @@ "integrity": "sha512-0/BnGCCfyUMkBpeDgWihanIAF9JmZhHBgUhEqzvf+adhNGLoP6TaiI5oF8oyb3I45P+PcnrqihSf01M0l0G5+Q==", "dev": true }, + "node_modules/lazystream": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.1.tgz", + "integrity": "sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==", + "dev": true, + "peer": true, + "dependencies": { + "readable-stream": "^2.0.5" + }, + "engines": { + "node": ">= 0.6.3" + } + }, "node_modules/lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", "dev": true }, + "node_modules/lodash.defaults": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", + "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==", + "dev": true, + "peer": true + }, + "node_modules/lodash.difference": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.difference/-/lodash.difference-4.5.0.tgz", + "integrity": "sha512-dS2j+W26TQ7taQBGN8Lbbq04ssV3emRw4NY58WErlTO29pIqS0HmoT5aJ9+TUQ1N3G+JOZSji4eugsWwGp9yPA==", + "dev": true, + "peer": true + }, + "node_modules/lodash.flatten": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", + "integrity": "sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==", + "dev": true, + "peer": true + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "dev": true, + "peer": true + }, + "node_modules/lodash.union": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.union/-/lodash.union-4.6.0.tgz", + "integrity": "sha512-c4pB2CdGrGdjMKYLA+XiRDO7Y0PRQbm/Gzg8qMj+QH+pFVAoTp5sBpO0odL3FjoPCGjK96p6qsP+yQoiLoOBcw==", + "dev": true, + "peer": true + }, "node_modules/logform": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/logform/-/logform-2.3.0.tgz", - "integrity": "sha512-graeoWUH2knKbGthMtuG1EfaSPMZFZBIrhuJHhkS5ZseFBrc7DupCzihOQAzsK/qIKPQaPJ/lFQFctILUY5ARQ==", + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/logform/-/logform-2.6.0.tgz", + "integrity": "sha512-1ulHeNPp6k/LD8H91o7VYFBng5i1BDE7HoKxVbZiGFidS1Rj65qcywLxX+pVfAPoQJEjRdvKcusKwOupHCVOVQ==", "dependencies": { - "colors": "^1.2.1", + "@colors/colors": "1.6.0", + "@types/triple-beam": "^1.3.2", "fecha": "^4.2.0", "ms": "^2.1.1", - "safe-stable-stringify": "^1.1.0", + "safe-stable-stringify": "^2.3.1", "triple-beam": "^1.3.0" + }, + "engines": { + "node": ">= 12.0.0" } }, "node_modules/lru-cache": { @@ -2445,30 +3019,30 @@ } }, "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", "dev": true, "dependencies": { - "brace-expansion": "^1.1.7" + "brace-expansion": "^2.0.1" }, "engines": { - "node": "*" + "node": ">=10" } }, "node_modules/minimist": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz", - "integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==", + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", "dev": true, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/minipass": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-4.0.1.tgz", - "integrity": "sha512-V9esFpNbK0arbN3fm2sxDKqMYgIp7XtVdE4Esj+PE4Qaaxdg1wIw48ITQIOn1sc8xXSmUviVL3cyjMqPlrVkiA==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", "dev": true, "engines": { "node": ">=8" @@ -2514,7 +3088,7 @@ "node_modules/modify-filename": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/modify-filename/-/modify-filename-1.1.0.tgz", - "integrity": "sha1-mi3sg4Bvuy2XXyK+7IWcoms5OqE=", + "integrity": "sha512-EickqnKq3kVVaZisYuCxhtKbZjInCuwgwZWyAmRIp1NTMhri7r3380/uqwrUHfaDiPzLVTuoNy4whX66bxPVog==", "engines": { "node": ">=0.10.0" } @@ -2541,6 +3115,16 @@ "dev": true, "optional": true }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/oauth-sign": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", @@ -2609,6 +3193,31 @@ "node": ">=8" } }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.2.tgz", + "integrity": "sha512-9hp3Vp2/hFQUiIwKo8XCeFVnrg8Pk3TYNPIR7tJADKi5YfcF7vEaK7avFHTlSy3kOKYaJQaalfEo6YuXdceBOQ==", + "dev": true, + "engines": { + "node": "14 || >=16.14" + } + }, "node_modules/pend": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", @@ -2620,22 +3229,25 @@ "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" }, "node_modules/plist": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/plist/-/plist-3.0.6.tgz", - "integrity": "sha512-WiIVYyrp8TD4w8yCvyeIr+lkmrGRd5u0VbRnU+tP/aRLxP/YadJUYOMZJ/6hIa3oUyVCsycXvtNRgd5XBJIbiA==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/plist/-/plist-3.1.0.tgz", + "integrity": "sha512-uysumyrvkUX0rX/dEVqt8gC3sTBzd4zoWfLeS29nb53imdaXVvLINYXTI2GNqzaMuvacNx4uJQ8+b3zXR0pkgQ==", "dev": true, "dependencies": { + "@xmldom/xmldom": "^0.8.8", "base64-js": "^1.5.1", "xmlbuilder": "^15.1.1" }, "engines": { - "node": ">=6" + "node": ">=10.4.0" } }, "node_modules/process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true, + "peer": true }, "node_modules/progress": { "version": "2.0.3", @@ -2646,6 +3258,19 @@ "node": ">=0.4.0" } }, + "node_modules/promise-retry": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz", + "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==", + "dev": true, + "dependencies": { + "err-code": "^2.0.2", + "retry": "^0.12.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/psl": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", @@ -2669,9 +3294,9 @@ } }, "node_modules/pupa": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pupa/-/pupa-2.0.1.tgz", - "integrity": "sha512-hEJH0s8PXLY/cdXh66tNEQGndDrIKNqNC5xmrysZy3i5C3oEoLna7YAOad+7u125+zH1HNXUmGEkrhb3c2VriA==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/pupa/-/pupa-2.1.1.tgz", + "integrity": "sha512-l1jNAspIBSFqbT+y+5FosojNpVpF94nlI+wDUpqP9enwOTfHx9f0gh5nB96vl+6yTpsJsypeNrwfzPrKuHB41A==", "dependencies": { "escape-goat": "^2.0.0" }, @@ -2700,11 +3325,12 @@ } }, "node_modules/read-config-file": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/read-config-file/-/read-config-file-6.2.0.tgz", - "integrity": "sha512-gx7Pgr5I56JtYz+WuqEbQHj/xWo+5Vwua2jhb1VwM4Wid5PqYmZ4i00ZB0YEGIfkVBsCv9UrjgyqCiQfS/Oosg==", + "version": "6.3.2", + "resolved": "https://registry.npmjs.org/read-config-file/-/read-config-file-6.3.2.tgz", + "integrity": "sha512-M80lpCjnE6Wt6zb98DoW8WHR09nzMSpu8XHtPkiTHrJ5Az9CybfeQhTJ8D7saeBHpGhLPIVyA8lcL6ZmdKwY6Q==", "dev": true, "dependencies": { + "config-file-ts": "^0.2.4", "dotenv": "^9.0.2", "dotenv-expand": "^5.1.0", "js-yaml": "^4.1.0", @@ -2727,6 +3353,8 @@ "version": "2.3.7", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "peer": true, "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -2737,6 +3365,16 @@ "util-deprecate": "~1.0.1" } }, + "node_modules/readdir-glob": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/readdir-glob/-/readdir-glob-1.1.3.tgz", + "integrity": "sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==", + "dev": true, + "peer": true, + "dependencies": { + "minimatch": "^5.1.0" + } + }, "node_modules/request": { "version": "2.88.2", "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", @@ -2782,19 +3420,13 @@ "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==", "dev": true }, - "node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "node_modules/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", "dev": true, - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "engines": { + "node": ">= 4" } }, "node_modules/roarr": { @@ -2821,9 +3453,12 @@ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" }, "node_modules/safe-stable-stringify": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-1.1.1.tgz", - "integrity": "sha512-ERq4hUjKDbJfE4+XtZLFPCDi8Vb1JqaxAPTxWFLBx8XcAlf9Bda/ZJdVezs/NAfsMQScyIlUMx+Yeu7P7rx5jw==" + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.4.3.tgz", + "integrity": "sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g==", + "engines": { + "node": ">=10" + } }, "node_modules/safer-buffer": { "version": "2.1.2", @@ -2840,9 +3475,9 @@ } }, "node_modules/sax": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.3.0.tgz", + "integrity": "sha512-0s+oAmw9zLl1V1cS9BtZN7JAd0cW5e0QH4W3LWEK6a4LaLEA2OTpGYWDY+6XasBLtz6wkm3u1xRw95mRuJ59WA==", "dev": true }, "node_modules/semver": { @@ -2904,6 +3539,18 @@ "node": ">=8" } }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/simple-swizzle": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", @@ -2913,24 +3560,15 @@ } }, "node_modules/simple-update-notifier": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-1.1.0.tgz", - "integrity": "sha512-VpsrsJSUcJEseSbMHkrsrAVSdvVS5I96Qo1QAQ4FxQ9wXFcB+pjj7FB7/us9+GcgfW4ziHtYMc1J0PLczb55mg==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz", + "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==", "dev": true, "dependencies": { - "semver": "~7.0.0" + "semver": "^7.5.3" }, "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/simple-update-notifier/node_modules/semver": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz", - "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==", - "dev": true, - "bin": { - "semver": "bin/semver.js" + "node": ">=10" } }, "node_modules/slice-ansi": { @@ -2946,14 +3584,6 @@ "node": ">=8" } }, - "node_modules/slice-ansi/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "engines": { - "node": ">=8" - } - }, "node_modules/smart-buffer": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", @@ -2968,7 +3598,7 @@ "node_modules/sort-keys": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-1.1.2.tgz", - "integrity": "sha1-RBttTTRnmPG05J6JIK37oOVD+a0=", + "integrity": "sha512-vzn8aSqKgytVik0iwdBEi+zevbTYZogewTUM6dtpmGwEcdzbub/TX4bCzRhebDCRC3QzXgJsLRKB2V/Oof7HXg==", "dependencies": { "is-plain-obj": "^1.0.0" }, @@ -2979,7 +3609,7 @@ "node_modules/sort-keys-length": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/sort-keys-length/-/sort-keys-length-1.0.1.tgz", - "integrity": "sha1-nLb09OnkgVWmqgZx7dM2/xR5oYg=", + "integrity": "sha512-GRbEOUqCxemTAk/b32F2xa8wDTs+Z1QHOkbhJDQTvv/6G3ZkbJ+frYWsTcc7cBB3Fu4wy4XlLCuNtJuMn7Gsvw==", "dependencies": { "sort-keys": "^1.0.0" }, @@ -3057,10 +3687,25 @@ "safe-buffer": "~5.1.0" } }, - "node_modules/string-width": { + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -3070,23 +3715,23 @@ "node": ">=8" } }, - "node_modules/string-width/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" - }, - "node_modules/string-width/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dependencies": { + "ansi-regex": "^5.0.1" + }, "engines": { "node": ">=8" } }, - "node_modules/strip-ansi": { + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, "dependencies": { "ansi-regex": "^5.0.1" }, @@ -3119,14 +3764,14 @@ } }, "node_modules/tar": { - "version": "6.1.13", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.13.tgz", - "integrity": "sha512-jdIBIN6LTIe2jqzay/2vtYLlBHa3JF42ot3h1dW8Q0PaAG4v8rm0cvpVePtau5C6OKXGGcgO9q2AMNSWxiLqKw==", + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", + "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", "dev": true, "dependencies": { "chownr": "^2.0.0", "fs-minipass": "^2.0.0", - "minipass": "^4.0.0", + "minipass": "^5.0.0", "minizlib": "^2.1.1", "mkdirp": "^1.0.3", "yallist": "^4.0.0" @@ -3135,6 +3780,38 @@ "node": ">=10" } }, + "node_modules/tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "dev": true, + "peer": true, + "dependencies": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tar-stream/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "peer": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/temp-file": { "version": "3.4.0", "resolved": "https://registry.npmjs.org/temp-file/-/temp-file-3.4.0.tgz", @@ -3172,9 +3849,9 @@ } }, "node_modules/temp-file/node_modules/universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", "dev": true, "engines": { "node": ">= 10.0.0" @@ -3205,15 +3882,12 @@ } }, "node_modules/tmp": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", - "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.3.tgz", + "integrity": "sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==", "dev": true, - "dependencies": { - "rimraf": "^3.0.0" - }, "engines": { - "node": ">=8.17.0" + "node": ">=14.14" } }, "node_modules/tmp-promise": { @@ -3238,9 +3912,12 @@ } }, "node_modules/triple-beam": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.3.0.tgz", - "integrity": "sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw==" + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.4.1.tgz", + "integrity": "sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==", + "engines": { + "node": ">= 14.0.0" + } }, "node_modules/truncate-utf8-bytes": { "version": "1.0.2", @@ -3280,6 +3957,25 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/typescript": { + "version": "5.4.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", + "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "devOptional": true + }, "node_modules/universalify": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", @@ -3310,9 +4006,9 @@ } }, "node_modules/utf8-byte-length": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/utf8-byte-length/-/utf8-byte-length-1.0.4.tgz", - "integrity": "sha512-4+wkEYLBbWxqTahEsWrhxepcoVOJ+1z5PGIjPZxRkytcdSUaNjIjBM7Xn8E+pdSuV7SzvWovBFA54FO0JSoqhA==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/utf8-byte-length/-/utf8-byte-length-1.0.5.tgz", + "integrity": "sha512-Xn0w3MtiQ6zoz2vFyUVruaCL53O/DwUvkEeOvj+uulMm0BkUGYWmBYVyElqZaSLhY6ZD0ulfU3aBra2aVT4xfA==", "dev": true }, "node_modules/util-deprecate": { @@ -3357,34 +4053,50 @@ } }, "node_modules/winston": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/winston/-/winston-3.3.3.tgz", - "integrity": "sha512-oEXTISQnC8VlSAKf1KYSSd7J6IWuRPQqDdo8eoRNaYKLvwSb5+79Z3Yi1lrl6KDpU6/VWaxpakDAtb1oQ4n9aw==", + "version": "3.13.0", + "resolved": "https://registry.npmjs.org/winston/-/winston-3.13.0.tgz", + "integrity": "sha512-rwidmA1w3SE4j0E5MuIufFhyJPBDG7Nu71RkZor1p2+qHvJSZ9GYDA81AyleQcZbh/+V6HjeBdfnTZJm9rSeQQ==", "dependencies": { + "@colors/colors": "^1.6.0", "@dabh/diagnostics": "^2.0.2", - "async": "^3.1.0", + "async": "^3.2.3", "is-stream": "^2.0.0", - "logform": "^2.2.0", + "logform": "^2.4.0", "one-time": "^1.0.0", "readable-stream": "^3.4.0", + "safe-stable-stringify": "^2.3.1", "stack-trace": "0.0.x", "triple-beam": "^1.3.0", - "winston-transport": "^4.4.0" + "winston-transport": "^4.7.0" }, "engines": { - "node": ">= 6.4.0" + "node": ">= 12.0.0" } }, "node_modules/winston-transport": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.4.0.tgz", - "integrity": "sha512-Lc7/p3GtqtqPBYYtS6KCN3c77/2QCev51DvcJKbkFPQNoj1sinkGwLGFDxkXY9J6p9+EPnYs+D90uwbnaiURTw==", + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.7.0.tgz", + "integrity": "sha512-ajBj65K5I7denzer2IYW6+2bNIVqLGDHqDw3Ow8Ohh+vdW+rv4MZ6eiDvHoKhfJFZ2auyN8byXieDDJ96ViONg==", "dependencies": { - "readable-stream": "^2.3.7", - "triple-beam": "^1.2.0" + "logform": "^2.3.2", + "readable-stream": "^3.6.0", + "triple-beam": "^1.3.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/winston-transport/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" }, "engines": { - "node": ">= 6.4.0" + "node": ">= 6" } }, "node_modules/winston/node_modules/readable-stream": { @@ -3417,6 +4129,24 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", @@ -3481,9 +4211,66 @@ "buffer-crc32": "~0.2.3", "fd-slicer": "~1.1.0" } + }, + "node_modules/zip-stream": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-4.1.1.tgz", + "integrity": "sha512-9qv4rlDiopXg4E69k+vMHjNN63YFMe9sZMrdlvKnCjlCRWeCBswPPMPUfx+ipsAWq1LXHe70RcbaHdJJpS6hyQ==", + "dev": true, + "peer": true, + "dependencies": { + "archiver-utils": "^3.0.4", + "compress-commons": "^4.1.2", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/zip-stream/node_modules/archiver-utils": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-3.0.4.tgz", + "integrity": "sha512-KVgf4XQVrTjhyWmx6cte4RxonPLR9onExufI1jhvw/MQ4BB6IsZD5gT8Lq+u/+pRkWna/6JoHpiQioaqFP5Rzw==", + "dev": true, + "peer": true, + "dependencies": { + "glob": "^7.2.3", + "graceful-fs": "^4.2.0", + "lazystream": "^1.0.0", + "lodash.defaults": "^4.2.0", + "lodash.difference": "^4.5.0", + "lodash.flatten": "^4.4.0", + "lodash.isplainobject": "^4.0.6", + "lodash.union": "^4.6.0", + "normalize-path": "^3.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/zip-stream/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "peer": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } } }, "dependencies": { + "@colors/colors": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz", + "integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==" + }, "@dabh/diagnostics": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.2.tgz", @@ -3504,6 +4291,38 @@ "ajv-keywords": "^3.4.1" } }, + "@electron/asar": { + "version": "3.2.10", + "resolved": "https://registry.npmjs.org/@electron/asar/-/asar-3.2.10.tgz", + "integrity": "sha512-mvBSwIBUeiRscrCeJE1LwctAriBj65eUDm0Pc11iE5gRwzkmsdbS7FnZ1XUWjpSeQWL1L5g12Fc/SchPM9DUOw==", + "dev": true, + "requires": { + "commander": "^5.0.0", + "glob": "^7.1.6", + "minimatch": "^3.0.4" + }, + "dependencies": { + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + } + } + }, "@electron/get": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/@electron/get/-/get-2.0.2.tgz", @@ -3649,48 +4468,222 @@ } } }, + "@electron/notarize": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@electron/notarize/-/notarize-2.2.1.tgz", + "integrity": "sha512-aL+bFMIkpR0cmmj5Zgy0LMKEpgy43/hw5zadEArgmAMWWlKc5buwFvFT9G/o/YJkvXAJm5q3iuTuLaiaXW39sg==", + "dev": true, + "requires": { + "debug": "^4.1.1", + "fs-extra": "^9.0.1", + "promise-retry": "^2.0.1" + }, + "dependencies": { + "fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dev": true, + "requires": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + } + }, + "jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6", + "universalify": "^2.0.0" + } + }, + "universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true + } + } + }, + "@electron/osx-sign": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@electron/osx-sign/-/osx-sign-1.0.5.tgz", + "integrity": "sha512-k9ZzUQtamSoweGQDV2jILiRIHUu7lYlJ3c6IEmjv1hC17rclE+eb9U+f6UFlOOETo0JzY1HNlXy4YOlCvl+Lww==", + "dev": true, + "requires": { + "compare-version": "^0.1.2", + "debug": "^4.3.4", + "fs-extra": "^10.0.0", + "isbinaryfile": "^4.0.8", + "minimist": "^1.2.6", + "plist": "^3.0.5" + }, + "dependencies": { + "fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + } + }, + "isbinaryfile": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-4.0.10.tgz", + "integrity": "sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw==", + "dev": true + }, + "jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6", + "universalify": "^2.0.0" + } + }, + "universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true + } + } + }, "@electron/universal": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@electron/universal/-/universal-1.2.1.tgz", - "integrity": "sha512-7323HyMh7KBAl/nPDppdLsC87G6RwRU02dy5FPeGB1eS7rUePh55+WNWiDPLhFQqqVPHzh77M69uhmoT8XnwMQ==", + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/@electron/universal/-/universal-1.5.1.tgz", + "integrity": "sha512-kbgXxyEauPJiQQUNG2VgUeyfQNFk6hBF11ISN2PNI6agUgPl55pv4eQmaqHzTAzchBvqZ2tQuRVaPStGf0mxGw==", "dev": true, "requires": { + "@electron/asar": "^3.2.1", "@malept/cross-spawn-promise": "^1.1.0", - "asar": "^3.1.0", "debug": "^4.3.1", - "dir-compare": "^2.4.0", + "dir-compare": "^3.0.0", "fs-extra": "^9.0.1", "minimatch": "^3.0.4", "plist": "^3.0.4" }, "dependencies": { + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, "fs-extra": { "version": "9.1.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", "dev": true, "requires": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + } + }, + "jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6", + "universalify": "^2.0.0" + } + }, + "minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true + } + } + }, + "@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "requires": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true + }, + "ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true + }, + "emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "requires": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" } }, - "jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", "dev": true, "requires": { - "graceful-fs": "^4.1.6", - "universalify": "^2.0.0" + "ansi-regex": "^6.0.1" } }, - "universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", - "dev": true + "wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "requires": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + } } } }, @@ -3738,13 +4731,20 @@ } }, "universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", "dev": true } } }, + "@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "optional": true + }, "@tootallnate/once": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", @@ -3764,9 +4764,9 @@ } }, "@types/debug": { - "version": "4.1.7", - "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.7.tgz", - "integrity": "sha512-9AonUzyTjXXhEOa0DnqpzZi6VHlqKMswga9EXjpXnnqxwLtdvPPtlO8evrI5D9S6asFRCQ6v+wpiUKbw+vKqyg==", + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", + "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", "dev": true, "requires": { "@types/ms": "*" @@ -3781,17 +4781,6 @@ "@types/node": "*" } }, - "@types/glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==", - "dev": true, - "optional": true, - "requires": { - "@types/minimatch": "*", - "@types/node": "*" - } - }, "@types/http-cache-semantics": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz", @@ -3807,29 +4796,25 @@ "@types/node": "*" } }, - "@types/minimatch": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-5.1.2.tgz", - "integrity": "sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==", - "dev": true, - "optional": true - }, "@types/ms": { - "version": "0.7.31", - "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.31.tgz", - "integrity": "sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==", + "version": "0.7.34", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.34.tgz", + "integrity": "sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==", "dev": true }, "@types/node": { - "version": "16.18.11", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.11.tgz", - "integrity": "sha512-3oJbGBUWuS6ahSnEq1eN2XrCyf4YsWI8OyCvo7c64zQJNplk3mO84t53o8lfTk+2ji59g5ycfc6qQ3fdHliHuA==", - "devOptional": true + "version": "20.12.12", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.12.tgz", + "integrity": "sha512-eWLDGF/FOSPtAvEqeRAQ4C8LSA7M1I7i0ky1I8U7kD1J5ITyW3AsRhQrKVoWf5pFKZ2kILsEGJhsI9r93PYnOw==", + "devOptional": true, + "requires": { + "undici-types": "~5.26.4" + } }, "@types/plist": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@types/plist/-/plist-3.0.2.tgz", - "integrity": "sha512-ULqvZNGMv0zRFvqn8/4LSPtnmN4MfhlPNtJCTpKuIIxGVGZ2rYWzFXrvEBoh9CVyqSE7D6YFRJ1hydLHI6kbWw==", + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@types/plist/-/plist-3.0.5.tgz", + "integrity": "sha512-E6OCaRmAe4WDmWNsL/9RMqdkkzDCY1etutkflWk4c+AcjDU07Pcz1fQwTX0TQz+Pxqn9i4L1TU3UFpjnrcDgxA==", "dev": true, "optional": true, "requires": { @@ -3846,28 +4831,18 @@ "@types/node": "*" } }, + "@types/triple-beam": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.5.tgz", + "integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==" + }, "@types/verror": { - "version": "1.10.6", - "resolved": "https://registry.npmjs.org/@types/verror/-/verror-1.10.6.tgz", - "integrity": "sha512-NNm+gdePAX1VGvPcGZCDKQZKYSiAWigKhKaz5KF94hG6f2s8de9Ow5+7AbXoeKxL8gavZfk4UquSAygOF2duEQ==", + "version": "1.10.10", + "resolved": "https://registry.npmjs.org/@types/verror/-/verror-1.10.10.tgz", + "integrity": "sha512-l4MM0Jppn18hb9xmM6wwD1uTdShpf9Pn80aXTStnK1C94gtPvJcV2FrDmbOQUAQfJ1cKZHktkQUDwEqaAKXMMg==", "dev": true, "optional": true }, - "@types/yargs": { - "version": "17.0.22", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.22.tgz", - "integrity": "sha512-pet5WJ9U8yPVRhkwuEIp5ktAeAqRZOq4UdAyWLWzxbtpyXnzbtLdKiXAjJzi/KLmPGS9wk86lUFWZFN6sISo4g==", - "dev": true, - "requires": { - "@types/yargs-parser": "*" - } - }, - "@types/yargs-parser": { - "version": "21.0.0", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz", - "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==", - "dev": true - }, "@types/yauzl": { "version": "2.9.1", "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.9.1.tgz", @@ -3877,10 +4852,16 @@ "@types/node": "*" } }, + "@xmldom/xmldom": { + "version": "0.8.10", + "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.10.tgz", + "integrity": "sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw==", + "dev": true + }, "7zip-bin": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/7zip-bin/-/7zip-bin-5.1.1.tgz", - "integrity": "sha512-sAP4LldeWNz0lNzmTird3uWfFDWWTeg6V/MsmyyLR9X1idwKBWIgt/ZvinqQldJm3LecKEs1emkbquO6PCiLVQ==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/7zip-bin/-/7zip-bin-5.2.0.tgz", + "integrity": "sha512-ukTPVhqG4jNzMro2qA9HSCSSVJN3aN7tlb+hfqYCt3ER0yWroeA2VR38MNrOHLQ/cVj+DaIMad0kFCtWWowh/A==", "dev": true }, "agent-base": { @@ -3935,36 +4916,37 @@ "dev": true }, "app-builder-lib": { - "version": "23.6.0", - "resolved": "https://registry.npmjs.org/app-builder-lib/-/app-builder-lib-23.6.0.tgz", - "integrity": "sha512-dQYDuqm/rmy8GSCE6Xl/3ShJg6Ab4bZJMT8KaTKGzT436gl1DN4REP3FCWfXoh75qGTJ+u+WsdnnpO9Jl8nyMA==", + "version": "24.13.3", + "resolved": "https://registry.npmjs.org/app-builder-lib/-/app-builder-lib-24.13.3.tgz", + "integrity": "sha512-FAzX6IBit2POXYGnTCT8YHFO/lr5AapAII6zzhQO3Rw4cEDOgK+t1xhLc5tNcKlicTHlo9zxIwnYCX9X2DLkig==", "dev": true, "requires": { "@develar/schema-utils": "~2.6.5", - "@electron/universal": "1.2.1", + "@electron/notarize": "2.2.1", + "@electron/osx-sign": "1.0.5", + "@electron/universal": "1.5.1", "@malept/flatpak-bundler": "^0.4.0", - "7zip-bin": "~5.1.1", + "@types/fs-extra": "9.0.13", "async-exit-hook": "^2.0.1", "bluebird-lst": "^1.0.9", - "builder-util": "23.6.0", - "builder-util-runtime": "9.1.1", + "builder-util": "24.13.1", + "builder-util-runtime": "9.2.4", "chromium-pickle-js": "^0.2.0", "debug": "^4.3.4", - "ejs": "^3.1.7", - "electron-osx-sign": "^0.6.0", - "electron-publish": "23.6.0", + "ejs": "^3.1.8", + "electron-publish": "24.13.1", "form-data": "^4.0.0", "fs-extra": "^10.1.0", "hosted-git-info": "^4.1.0", "is-ci": "^3.0.0", - "isbinaryfile": "^4.0.10", + "isbinaryfile": "^5.0.0", "js-yaml": "^4.1.0", "lazy-val": "^1.0.5", - "minimatch": "^3.1.2", - "read-config-file": "6.2.0", + "minimatch": "^5.1.1", + "read-config-file": "6.3.2", "sanitize-filename": "^1.6.3", - "semver": "^7.3.7", - "tar": "^6.1.11", + "semver": "^7.3.8", + "tar": "^6.1.12", "temp-file": "^3.4.0" }, "dependencies": { @@ -4001,32 +4983,68 @@ } }, "universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", "dev": true } } }, + "archiver": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/archiver/-/archiver-5.3.2.tgz", + "integrity": "sha512-+25nxyyznAXF7Nef3y0EbBeqmGZgeN/BxHX29Rs39djAfaFalmQ89SE6CWyDCHzGL0yt/ycBtNOmGTW0FyGWNw==", + "dev": true, + "peer": true, + "requires": { + "archiver-utils": "^2.1.0", + "async": "^3.2.4", + "buffer-crc32": "^0.2.1", + "readable-stream": "^3.6.0", + "readdir-glob": "^1.1.2", + "tar-stream": "^2.2.0", + "zip-stream": "^4.1.0" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "peer": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } + } + }, + "archiver-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-2.1.0.tgz", + "integrity": "sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw==", + "dev": true, + "peer": true, + "requires": { + "glob": "^7.1.4", + "graceful-fs": "^4.2.0", + "lazystream": "^1.0.0", + "lodash.defaults": "^4.2.0", + "lodash.difference": "^4.5.0", + "lodash.flatten": "^4.4.0", + "lodash.isplainobject": "^4.0.6", + "lodash.union": "^4.6.0", + "normalize-path": "^3.0.0", + "readable-stream": "^2.0.0" + } + }, "argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true }, - "asar": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/asar/-/asar-3.2.0.tgz", - "integrity": "sha512-COdw2ZQvKdFGFxXwX3oYh2/sOsJWJegrdJCGxnN4MZ7IULgRBp9P6665aqj9z1v9VwP4oP1hRBojRDQ//IGgAg==", - "dev": true, - "requires": { - "@types/glob": "^7.1.1", - "chromium-pickle-js": "^0.2.0", - "commander": "^5.0.0", - "glob": "^7.1.6", - "minimatch": "^3.0.4" - } - }, "asn1": { "version": "0.2.4", "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", @@ -4046,9 +5064,9 @@ "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==" }, "async": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.3.tgz", - "integrity": "sha512-spZRyzKL5l5BZQrr/6m/SqFdBN0q3OCI0f9rjfBzCMBIP4p75P620rR3gTmaksNOhmzgdxcaxdNfMy6anrbM0g==" + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz", + "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==" }, "async-exit-hook": { "version": "2.0.1", @@ -4097,6 +5115,32 @@ "tweetnacl": "^0.14.3" } }, + "bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dev": true, + "peer": true, + "requires": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "peer": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } + } + }, "bluebird": { "version": "3.7.2", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", @@ -4120,13 +5164,12 @@ "optional": true }, "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "dev": true, "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "balanced-match": "^1.0.0" } }, "buffer": { @@ -4134,43 +5177,20 @@ "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", "dev": true, - "optional": true, "requires": { "base64-js": "^1.3.1", "ieee754": "^1.1.13" } }, - "buffer-alloc": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz", - "integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==", - "dev": true, - "requires": { - "buffer-alloc-unsafe": "^1.1.0", - "buffer-fill": "^1.0.0" - } - }, - "buffer-alloc-unsafe": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz", - "integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==", - "dev": true - }, "buffer-crc32": { "version": "0.2.13", "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=" }, "buffer-equal": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/buffer-equal/-/buffer-equal-1.0.0.tgz", - "integrity": "sha512-tcBWO2Dl4e7Asr9hTGcpVrCe+F7DubpmqWCTbj4FHLmjqO2hIaC383acQubWtRJhdceqs5uBHs6Es+Sk//RKiQ==", - "dev": true - }, - "buffer-fill": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz", - "integrity": "sha512-T7zexNBwiiaCOGDg9xNX9PBmjrubblRkENuptryuI64URkXDFum9il/JGL8Lm8wYfAXpredVXXZz7eMHilimiQ==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal/-/buffer-equal-1.0.1.tgz", + "integrity": "sha512-QoV3ptgEaQpvVwbXdSO39iqPQTCxSF7A5U99AxbHYqUdCizL/lH2Z0A2y6nbZucxMEOtNyZfG2s6gsVugGpKkg==", "dev": true }, "buffer-from": { @@ -4180,23 +5200,22 @@ "dev": true }, "builder-util": { - "version": "23.6.0", - "resolved": "https://registry.npmjs.org/builder-util/-/builder-util-23.6.0.tgz", - "integrity": "sha512-QiQHweYsh8o+U/KNCZFSvISRnvRctb8m/2rB2I1JdByzvNKxPeFLlHFRPQRXab6aYeXc18j9LpsDLJ3sGQmWTQ==", + "version": "24.13.1", + "resolved": "https://registry.npmjs.org/builder-util/-/builder-util-24.13.1.tgz", + "integrity": "sha512-NhbCSIntruNDTOVI9fdXz0dihaqX2YuE1D6zZMrwiErzH4ELZHE6mdiB40wEgZNprDia+FghRFgKoAqMZRRjSA==", "dev": true, "requires": { "@types/debug": "^4.1.6", - "@types/fs-extra": "^9.0.11", - "7zip-bin": "~5.1.1", + "7zip-bin": "~5.2.0", "app-builder-bin": "4.0.0", "bluebird-lst": "^1.0.9", - "builder-util-runtime": "9.1.1", - "chalk": "^4.1.1", + "builder-util-runtime": "9.2.4", + "chalk": "^4.1.2", "cross-spawn": "^7.0.3", "debug": "^4.3.4", - "fs-extra": "^10.0.0", + "fs-extra": "^10.1.0", "http-proxy-agent": "^5.0.0", - "https-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.1", "is-ci": "^3.0.0", "js-yaml": "^4.1.0", "source-map-support": "^0.5.19", @@ -4226,17 +5245,17 @@ } }, "universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", "dev": true } } }, "builder-util-runtime": { - "version": "9.1.1", - "resolved": "https://registry.npmjs.org/builder-util-runtime/-/builder-util-runtime-9.1.1.tgz", - "integrity": "sha512-azRhYLEoDvRDR8Dhis4JatELC/jUvYjm4cVSj7n9dauGTOM2eeNn9KS0z6YA6oDsjI1xphjNbY6PZZeHPzzqaw==", + "version": "9.2.4", + "resolved": "https://registry.npmjs.org/builder-util-runtime/-/builder-util-runtime-9.2.4.tgz", + "integrity": "sha512-upp+biKpN/XZMLim7aguUyW8s0FUpDvOtK6sbanMFDAMBzpHDqdhgVYm6zc9HJ6nWo7u2Lxk60i2M6Jd3aiNrA==", "dev": true, "requires": { "debug": "^4.3.4", @@ -4277,9 +5296,9 @@ "dev": true }, "ci-info": { - "version": "3.7.1", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.7.1.tgz", - "integrity": "sha512-4jYS4MOAaCIStSRwiuxc4B8MYhIe676yO1sYGzARnjXkWpmzZMMYxY6zu8WYWDhSuth5zhrQ1rhNSibyyvv4/w==", + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", + "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", "dev": true }, "cli-truncate": { @@ -4357,11 +5376,6 @@ "simple-swizzle": "^0.2.2" } }, - "colors": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", - "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==" - }, "colorspace": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.2.tgz", @@ -4391,12 +5405,79 @@ "integrity": "sha512-pJDh5/4wrEnXX/VWRZvruAGHkzKdr46z11OlTPN+VrATlWWhSKewNCJ1futCO5C7eJB3nPMFZA1LeYtcFboZ2A==", "dev": true }, + "compress-commons": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-4.1.2.tgz", + "integrity": "sha512-D3uMHtGc/fcO1Gt1/L7i1e33VOvD4A9hfQLP+6ewd+BvG/gQ84Yh4oftEhAdjSMgBgwGL+jsppT7JYNpo6MHHg==", + "dev": true, + "peer": true, + "requires": { + "buffer-crc32": "^0.2.13", + "crc32-stream": "^4.0.2", + "normalize-path": "^3.0.0", + "readable-stream": "^3.6.0" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "peer": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } + } + }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true }, + "config-file-ts": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/config-file-ts/-/config-file-ts-0.2.6.tgz", + "integrity": "sha512-6boGVaglwblBgJqGyxm4+xCmEGcWgnWHSWHY5jad58awQhB6gftq0G8HbzU39YqCIYHMLAiL1yjwiZ36m/CL8w==", + "dev": true, + "requires": { + "glob": "^10.3.10", + "typescript": "^5.3.3" + }, + "dependencies": { + "glob": { + "version": "10.3.16", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.16.tgz", + "integrity": "sha512-JDKXl1DiuuHJ6fVS2FXjownaavciiHNUU4mOvV/B793RLh05vZL1rcPnCSaOgv1hDT6RDlY7AB7ZUvFYAtPgAw==", + "dev": true, + "requires": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.1", + "minipass": "^7.0.4", + "path-scurry": "^1.11.0" + } + }, + "minimatch": { + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", + "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + } + }, + "minipass": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.1.tgz", + "integrity": "sha512-UZ7eQ+h8ywIRAW1hIEl2AqdwzJucU/Kp59+8kkZeSvafXhZjul247BvIJjEVFVeON6d7lM46XX1HXCduKAS8VA==", + "dev": true + } + } + }, "core-util-is": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", @@ -4412,6 +5493,38 @@ "buffer": "^5.1.0" } }, + "crc-32": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", + "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", + "dev": true, + "peer": true + }, + "crc32-stream": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-4.0.3.tgz", + "integrity": "sha512-NT7w2JVU7DFroFdYkeq8cywxrgjPHWkdX1wjpRQXPX5Asews3tA+Ght6lddQO5Mkumffp3X7GEqku3epj2toIw==", + "dev": true, + "peer": true, + "requires": { + "crc-32": "^1.2.0", + "readable-stream": "^3.4.0" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "peer": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } + } + }, "cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -4463,36 +5576,29 @@ "optional": true }, "dir-compare": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/dir-compare/-/dir-compare-2.4.0.tgz", - "integrity": "sha512-l9hmu8x/rjVC9Z2zmGzkhOEowZvW7pmYws5CWHutg8u1JgvsKWMx7Q/UODeu4djLZ4FgW5besw5yvMQnBHzuCA==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/dir-compare/-/dir-compare-3.3.0.tgz", + "integrity": "sha512-J7/et3WlGUCxjdnD3HAAzQ6nsnc0WL6DD7WcwJb7c39iH1+AWfg+9OqzJNaI6PkBwBvm1mhZNL9iY/nRiZXlPg==", "dev": true, "requires": { - "buffer-equal": "1.0.0", - "colors": "1.0.3", - "commander": "2.9.0", - "minimatch": "3.0.4" + "buffer-equal": "^1.0.0", + "minimatch": "^3.0.4" }, "dependencies": { - "colors": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz", - "integrity": "sha512-pFGrxThWcWQ2MsAz6RtgeWe4NK2kUE1WfsrvvlctdII745EW9I0yflqhe7++M5LEc7bV2c/9/5zc8sFcpL0Drw==", - "dev": true - }, - "commander": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.9.0.tgz", - "integrity": "sha512-bmkUukX8wAOjHdN26xj5c4ctEV22TQ7dQYhSmuckKhToXrkUn0iIaolHdIxYYqD55nhpSPA9zPQ1yP57GdXP2A==", + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, "requires": { - "graceful-readlink": ">= 1.0.0" + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, "requires": { "brace-expansion": "^1.1.7" @@ -4501,16 +5607,16 @@ } }, "dmg-builder": { - "version": "23.6.0", - "resolved": "https://registry.npmjs.org/dmg-builder/-/dmg-builder-23.6.0.tgz", - "integrity": "sha512-jFZvY1JohyHarIAlTbfQOk+HnceGjjAdFjVn3n8xlDWKsYNqbO4muca6qXEZTfGXeQMG7TYim6CeS5XKSfSsGA==", + "version": "24.13.3", + "resolved": "https://registry.npmjs.org/dmg-builder/-/dmg-builder-24.13.3.tgz", + "integrity": "sha512-rcJUkMfnJpfCboZoOOPf4L29TRtEieHNOeAbYPWPxlaBw/Z1RKrRA86dOI9rwaI4tQSc/RD82zTNHprfUHXsoQ==", "dev": true, "requires": { - "app-builder-lib": "23.6.0", - "builder-util": "23.6.0", - "builder-util-runtime": "9.1.1", + "app-builder-lib": "24.13.3", + "builder-util": "24.13.1", + "builder-util-runtime": "9.2.4", "dmg-license": "^1.0.11", - "fs-extra": "^10.0.0", + "fs-extra": "^10.1.0", "iconv-lite": "^0.6.2", "js-yaml": "^4.1.0" }, @@ -4537,9 +5643,9 @@ } }, "universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", "dev": true } } @@ -4573,6 +5679,12 @@ "integrity": "sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==", "dev": true }, + "eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true + }, "ecc-jsbn": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", @@ -4583,43 +5695,42 @@ } }, "ejs": { - "version": "3.1.8", - "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.8.tgz", - "integrity": "sha512-/sXZeMlhS0ArkfX2Aw780gJzXSMPnKjtspYZv+f3NiKLlubezAHDU5+9xz6gd3/NhG3txQCo6xlglmTS+oTGEQ==", + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", + "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", "dev": true, "requires": { "jake": "^10.8.5" } }, "electron": { - "version": "22.1.0", - "resolved": "https://registry.npmjs.org/electron/-/electron-22.1.0.tgz", - "integrity": "sha512-wz5s4N6V7zyKm4YQmXJImFoxO1Doai32ShYm0FzOLPBMwLMdQBV+REY+j1opRx0KJ9xJEIdjYgcA8OSw6vx3pA==", + "version": "30.0.6", + "resolved": "https://registry.npmjs.org/electron/-/electron-30.0.6.tgz", + "integrity": "sha512-PkhEPFdpYcTzjAO3gMHZ+map7g2+xCrMDedo/L1i0ir2BRXvAB93IkTJX497U6Srb/09r2cFt+k20VPNVCdw3Q==", "dev": true, "requires": { "@electron/get": "^2.0.0", - "@types/node": "^16.11.26", + "@types/node": "^20.9.0", "extract-zip": "^2.0.1" } }, "electron-builder": { - "version": "23.6.0", - "resolved": "https://registry.npmjs.org/electron-builder/-/electron-builder-23.6.0.tgz", - "integrity": "sha512-y8D4zO+HXGCNxFBV/JlyhFnoQ0Y0K7/sFH+XwIbj47pqaW8S6PGYQbjoObolKBR1ddQFPt4rwp4CnwMJrW3HAw==", + "version": "24.13.3", + "resolved": "https://registry.npmjs.org/electron-builder/-/electron-builder-24.13.3.tgz", + "integrity": "sha512-yZSgVHft5dNVlo31qmJAe4BVKQfFdwpRw7sFp1iQglDRCDD6r22zfRJuZlhtB5gp9FHUxCMEoWGq10SkCnMAIg==", "dev": true, "requires": { - "@types/yargs": "^17.0.1", - "app-builder-lib": "23.6.0", - "builder-util": "23.6.0", - "builder-util-runtime": "9.1.1", - "chalk": "^4.1.1", - "dmg-builder": "23.6.0", - "fs-extra": "^10.0.0", + "app-builder-lib": "24.13.3", + "builder-util": "24.13.1", + "builder-util-runtime": "9.2.4", + "chalk": "^4.1.2", + "dmg-builder": "24.13.3", + "fs-extra": "^10.1.0", "is-ci": "^3.0.0", "lazy-val": "^1.0.5", - "read-config-file": "6.2.0", - "simple-update-notifier": "^1.0.7", - "yargs": "^17.5.1" + "read-config-file": "6.3.2", + "simple-update-notifier": "2.0.0", + "yargs": "^17.6.2" }, "dependencies": { "fs-extra": { @@ -4651,14 +5762,59 @@ } } }, + "electron-builder-squirrel-windows": { + "version": "24.13.3", + "resolved": "https://registry.npmjs.org/electron-builder-squirrel-windows/-/electron-builder-squirrel-windows-24.13.3.tgz", + "integrity": "sha512-oHkV0iogWfyK+ah9ZIvMDpei1m9ZRpdXcvde1wTpra2U8AFDNNpqJdnin5z+PM1GbQ5BoaKCWas2HSjtR0HwMg==", + "dev": true, + "peer": true, + "requires": { + "app-builder-lib": "24.13.3", + "archiver": "^5.3.1", + "builder-util": "24.13.1", + "fs-extra": "^10.1.0" + }, + "dependencies": { + "fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "peer": true, + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + } + }, + "jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "peer": true, + "requires": { + "graceful-fs": "^4.1.6", + "universalify": "^2.0.0" + } + }, + "universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "peer": true + } + } + }, "electron-context-menu": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/electron-context-menu/-/electron-context-menu-2.3.0.tgz", - "integrity": "sha512-XYsYkNY+jvX4C5o09qMuZoKL6e9frnQzBFehZSIiKp6zK0u3XYowJYDyK3vDKKZxYsOIGiE/Gbx40jERC03Ctw==", + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/electron-context-menu/-/electron-context-menu-3.6.1.tgz", + "integrity": "sha512-lcpO6tzzKUROeirhzBjdBWNqayEThmdW+2I2s6H6QMrwqTVyT3EK47jW3Nxm60KTxl5/bWfEoIruoUNn57/QkQ==", "requires": { - "cli-truncate": "^2.0.0", - "electron-dl": "^3.0.0", - "electron-is-dev": "^1.0.1" + "cli-truncate": "^2.1.0", + "electron-dl": "^3.2.1", + "electron-is-dev": "^2.0.0" } }, "electron-default-menu": { @@ -4667,9 +5823,9 @@ "integrity": "sha512-YAL/UNR3kPG58wOOlmDpTG3i6+bzwhHx6NllIOaLuVrU7uYifeYGGdk5IH2Hap4wVEx2YTA8cqQ2PGSplYwDWQ==" }, "electron-dl": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/electron-dl/-/electron-dl-3.0.2.tgz", - "integrity": "sha512-pRgE9Jbhoo5z6Vk3qi+vIrfpMDlCp2oB1UeR96SMnsfz073jj0AZGQwp69EdIcEvlUlwBSGyJK8Jt6OB6JLn+g==", + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/electron-dl/-/electron-dl-3.5.2.tgz", + "integrity": "sha512-i104cl+u8yJ0lhpRAtUWfeGuWuL1PL6TBiw2gLf0MMIBjfgE485Ags2mcySx4uWU9P9uj/vsD3jd7X+w1lzZxw==", "requires": { "ext-name": "^5.0.0", "pupa": "^2.0.1", @@ -4677,69 +5833,29 @@ } }, "electron-is-dev": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/electron-is-dev/-/electron-is-dev-1.2.0.tgz", - "integrity": "sha512-R1oD5gMBPS7PVU8gJwH6CtT0e6VSoD0+SzSnYpNm+dBkcijgA+K7VAMHDfnRq/lkKPZArpzplTW6jfiMYosdzw==" - }, - "electron-osx-sign": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/electron-osx-sign/-/electron-osx-sign-0.6.0.tgz", - "integrity": "sha512-+hiIEb2Xxk6eDKJ2FFlpofCnemCbjbT5jz+BKGpVBrRNT3kWTGs4DfNX6IzGwgi33hUcXF+kFs9JW+r6Wc1LRg==", - "dev": true, - "requires": { - "bluebird": "^3.5.0", - "compare-version": "^0.1.2", - "debug": "^2.6.8", - "isbinaryfile": "^3.0.2", - "minimist": "^1.2.0", - "plist": "^3.0.1" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "isbinaryfile": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-3.0.3.tgz", - "integrity": "sha512-8cJBL5tTd2OS0dM4jz07wQd5g0dCCqIhUxPIGtZfa5L6hWlvV5MHTITy/DBAsF+Oe2LS1X3krBUhNwaGUWpWxw==", - "dev": true, - "requires": { - "buffer-alloc": "^1.2.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true - } - } + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/electron-is-dev/-/electron-is-dev-2.0.0.tgz", + "integrity": "sha512-3X99K852Yoqu9AcW50qz3ibYBWY79/pBhlMCab8ToEWS48R0T9tyxRiQhwylE7zQdXrMnx2JKqUJyMPmt5FBqA==" }, "electron-progressbar": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/electron-progressbar/-/electron-progressbar-2.0.1.tgz", - "integrity": "sha512-+N60GX2q+KH5OvZXxwtjMTZB/1AyxriFd95vOnR3sOfNpvz+30LMsM0a9SnEivZE6N8Djy7F3z4TY8pLs8aopw==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/electron-progressbar/-/electron-progressbar-2.2.1.tgz", + "integrity": "sha512-LQ9bxM3Tf5PG/1QngY8ywvht7IKvQ8tEIra8uh3RkLASqN/GYvr4r0uU9qz38r0Zn72UcouYzumKDfLwoI/rsw==", "requires": { "extend": "^3.0.1" } }, "electron-publish": { - "version": "23.6.0", - "resolved": "https://registry.npmjs.org/electron-publish/-/electron-publish-23.6.0.tgz", - "integrity": "sha512-jPj3y+eIZQJF/+t5SLvsI5eS4mazCbNYqatv5JihbqOstIM13k0d1Z3vAWntvtt13Itl61SO6seicWdioOU5dg==", + "version": "24.13.1", + "resolved": "https://registry.npmjs.org/electron-publish/-/electron-publish-24.13.1.tgz", + "integrity": "sha512-2ZgdEqJ8e9D17Hwp5LEq5mLQPjqU3lv/IALvgp+4W8VeNhryfGhYEQC/PgDPMrnWUp+l60Ou5SJLsu+k4mhQ8A==", "dev": true, "requires": { "@types/fs-extra": "^9.0.11", - "builder-util": "23.6.0", - "builder-util-runtime": "9.1.1", - "chalk": "^4.1.1", - "fs-extra": "^10.0.0", + "builder-util": "24.13.1", + "builder-util-runtime": "9.2.4", + "chalk": "^4.1.2", + "fs-extra": "^10.1.0", "lazy-val": "^1.0.5", "mime": "^2.5.2" }, @@ -4766,13 +5882,18 @@ } }, "universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", "dev": true } } }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, "enabled": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", @@ -4792,6 +5913,12 @@ "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", "dev": true }, + "err-code": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", + "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==", + "dev": true + }, "es6-error": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", @@ -4884,9 +6011,9 @@ } }, "fecha": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.1.tgz", - "integrity": "sha512-MMMQ0ludy/nBs1/o0zVOiKTpG7qMbonKUzjJgQFEuvq6INZ1OraKPRAWkBq5vlKLOUMpmNYG1JoN3oDPUQ9m3Q==" + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz", + "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==" }, "filelist": { "version": "1.0.4", @@ -4895,26 +6022,6 @@ "dev": true, "requires": { "minimatch": "^5.0.1" - }, - "dependencies": { - "brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0" - } - }, - "minimatch": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", - "dev": true, - "requires": { - "brace-expansion": "^2.0.1" - } - } } }, "fn.name": { @@ -4922,6 +6029,16 @@ "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==" }, + "foreground-child": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", + "integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + } + }, "forever-agent": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", @@ -4937,6 +6054,13 @@ "mime-types": "^2.1.12" } }, + "fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "dev": true, + "peer": true + }, "fs-extra": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", @@ -5019,6 +6143,27 @@ "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" + }, + "dependencies": { + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + } } }, "global-agent": { @@ -5052,12 +6197,6 @@ "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", "dev": true }, - "graceful-readlink": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz", - "integrity": "sha512-8tLu60LgxF6XpdbK8OW3FA+IfTNBn1ZHGHKF4KQbEeSkajYw5PlYJcKluntgegDPTg8UkHjpet1T82vk6TQ68w==", - "dev": true - }, "har-schema": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", @@ -5185,8 +6324,7 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "dev": true, - "optional": true + "dev": true }, "inflight": { "version": "1.0.6", @@ -5217,10 +6355,15 @@ "ci-info": "^3.2.0" } }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" + }, "is-plain-obj": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", - "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=" + "integrity": "sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==" }, "is-stream": { "version": "2.0.1", @@ -5235,12 +6378,14 @@ "isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true, + "peer": true }, "isbinaryfile": { - "version": "4.0.10", - "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-4.0.10.tgz", - "integrity": "sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw==", + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-5.0.2.tgz", + "integrity": "sha512-GvcjojwonMjWbTkfMpnVHVqXW/wKMYDfEpY94/8zy8HFMOqb/VL6oeONq9v87q4ttVlaTLnGXnJD4B5B1OTGIg==", "dev": true }, "isexe": { @@ -5254,16 +6399,47 @@ "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" }, + "jackspeak": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.1.2.tgz", + "integrity": "sha512-kWmLKn2tRtfYMF/BakihVVRzBKOxz4gJMiL2Rj91WnAB5TPZumSH99R/Yf1qE1u4uRimvCSJfm6hnxohXeEXjQ==", + "dev": true, + "requires": { + "@isaacs/cliui": "^8.0.2", + "@pkgjs/parseargs": "^0.11.0" + } + }, "jake": { - "version": "10.8.5", - "resolved": "https://registry.npmjs.org/jake/-/jake-10.8.5.tgz", - "integrity": "sha512-sVpxYeuAhWt0OTWITwT98oyV0GsXyMlXCF+3L1SuafBVUIr/uILGRB+NqwkzhgXKvoJpDIpQvqkUALgdmQsQxw==", + "version": "10.9.1", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.1.tgz", + "integrity": "sha512-61btcOHNnLnsOdtLgA5efqQWjnSi/vow5HbI7HMdKKWqvrKR1bLK3BPlJn9gcSaP2ewuamUSMB5XEy76KUIS2w==", "dev": true, "requires": { "async": "^3.2.3", "chalk": "^4.0.2", - "filelist": "^1.0.1", - "minimatch": "^3.0.4" + "filelist": "^1.0.4", + "minimatch": "^3.1.2" + }, + "dependencies": { + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + } } }, "js-yaml": { @@ -5332,21 +6508,67 @@ "integrity": "sha512-0/BnGCCfyUMkBpeDgWihanIAF9JmZhHBgUhEqzvf+adhNGLoP6TaiI5oF8oyb3I45P+PcnrqihSf01M0l0G5+Q==", "dev": true }, + "lazystream": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.1.tgz", + "integrity": "sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==", + "dev": true, + "peer": true, + "requires": { + "readable-stream": "^2.0.5" + } + }, "lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", "dev": true }, + "lodash.defaults": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", + "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==", + "dev": true, + "peer": true + }, + "lodash.difference": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.difference/-/lodash.difference-4.5.0.tgz", + "integrity": "sha512-dS2j+W26TQ7taQBGN8Lbbq04ssV3emRw4NY58WErlTO29pIqS0HmoT5aJ9+TUQ1N3G+JOZSji4eugsWwGp9yPA==", + "dev": true, + "peer": true + }, + "lodash.flatten": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", + "integrity": "sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==", + "dev": true, + "peer": true + }, + "lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "dev": true, + "peer": true + }, + "lodash.union": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.union/-/lodash.union-4.6.0.tgz", + "integrity": "sha512-c4pB2CdGrGdjMKYLA+XiRDO7Y0PRQbm/Gzg8qMj+QH+pFVAoTp5sBpO0odL3FjoPCGjK96p6qsP+yQoiLoOBcw==", + "dev": true, + "peer": true + }, "logform": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/logform/-/logform-2.3.0.tgz", - "integrity": "sha512-graeoWUH2knKbGthMtuG1EfaSPMZFZBIrhuJHhkS5ZseFBrc7DupCzihOQAzsK/qIKPQaPJ/lFQFctILUY5ARQ==", + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/logform/-/logform-2.6.0.tgz", + "integrity": "sha512-1ulHeNPp6k/LD8H91o7VYFBng5i1BDE7HoKxVbZiGFidS1Rj65qcywLxX+pVfAPoQJEjRdvKcusKwOupHCVOVQ==", "requires": { - "colors": "^1.2.1", + "@colors/colors": "1.6.0", + "@types/triple-beam": "^1.3.2", "fecha": "^4.2.0", "ms": "^2.1.1", - "safe-stable-stringify": "^1.1.0", + "safe-stable-stringify": "^2.3.1", "triple-beam": "^1.3.0" } }, @@ -5395,24 +6617,24 @@ "dev": true }, "minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", "dev": true, "requires": { - "brace-expansion": "^1.1.7" + "brace-expansion": "^2.0.1" } }, "minimist": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz", - "integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==", + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", "dev": true }, "minipass": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-4.0.1.tgz", - "integrity": "sha512-V9esFpNbK0arbN3fm2sxDKqMYgIp7XtVdE4Esj+PE4Qaaxdg1wIw48ITQIOn1sc8xXSmUviVL3cyjMqPlrVkiA==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", "dev": true }, "minizlib": { @@ -5445,7 +6667,7 @@ "modify-filename": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/modify-filename/-/modify-filename-1.1.0.tgz", - "integrity": "sha1-mi3sg4Bvuy2XXyK+7IWcoms5OqE=" + "integrity": "sha512-EickqnKq3kVVaZisYuCxhtKbZjInCuwgwZWyAmRIp1NTMhri7r3380/uqwrUHfaDiPzLVTuoNy4whX66bxPVog==" }, "ms": { "version": "2.1.2", @@ -5469,6 +6691,13 @@ "dev": true, "optional": true }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "peer": true + }, "oauth-sign": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", @@ -5519,6 +6748,24 @@ "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "dev": true }, + "path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "requires": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "dependencies": { + "lru-cache": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.2.tgz", + "integrity": "sha512-9hp3Vp2/hFQUiIwKo8XCeFVnrg8Pk3TYNPIR7tJADKi5YfcF7vEaK7avFHTlSy3kOKYaJQaalfEo6YuXdceBOQ==", + "dev": true + } + } + }, "pend": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", @@ -5530,11 +6777,12 @@ "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" }, "plist": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/plist/-/plist-3.0.6.tgz", - "integrity": "sha512-WiIVYyrp8TD4w8yCvyeIr+lkmrGRd5u0VbRnU+tP/aRLxP/YadJUYOMZJ/6hIa3oUyVCsycXvtNRgd5XBJIbiA==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/plist/-/plist-3.1.0.tgz", + "integrity": "sha512-uysumyrvkUX0rX/dEVqt8gC3sTBzd4zoWfLeS29nb53imdaXVvLINYXTI2GNqzaMuvacNx4uJQ8+b3zXR0pkgQ==", "dev": true, "requires": { + "@xmldom/xmldom": "^0.8.8", "base64-js": "^1.5.1", "xmlbuilder": "^15.1.1" } @@ -5542,7 +6790,9 @@ "process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true, + "peer": true }, "progress": { "version": "2.0.3", @@ -5550,6 +6800,16 @@ "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", "dev": true }, + "promise-retry": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz", + "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==", + "dev": true, + "requires": { + "err-code": "^2.0.2", + "retry": "^0.12.0" + } + }, "psl": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", @@ -5570,9 +6830,9 @@ "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" }, "pupa": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pupa/-/pupa-2.0.1.tgz", - "integrity": "sha512-hEJH0s8PXLY/cdXh66tNEQGndDrIKNqNC5xmrysZy3i5C3oEoLna7YAOad+7u125+zH1HNXUmGEkrhb3c2VriA==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/pupa/-/pupa-2.1.1.tgz", + "integrity": "sha512-l1jNAspIBSFqbT+y+5FosojNpVpF94nlI+wDUpqP9enwOTfHx9f0gh5nB96vl+6yTpsJsypeNrwfzPrKuHB41A==", "requires": { "escape-goat": "^2.0.0" } @@ -5589,11 +6849,12 @@ "dev": true }, "read-config-file": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/read-config-file/-/read-config-file-6.2.0.tgz", - "integrity": "sha512-gx7Pgr5I56JtYz+WuqEbQHj/xWo+5Vwua2jhb1VwM4Wid5PqYmZ4i00ZB0YEGIfkVBsCv9UrjgyqCiQfS/Oosg==", + "version": "6.3.2", + "resolved": "https://registry.npmjs.org/read-config-file/-/read-config-file-6.3.2.tgz", + "integrity": "sha512-M80lpCjnE6Wt6zb98DoW8WHR09nzMSpu8XHtPkiTHrJ5Az9CybfeQhTJ8D7saeBHpGhLPIVyA8lcL6ZmdKwY6Q==", "dev": true, "requires": { + "config-file-ts": "^0.2.4", "dotenv": "^9.0.2", "dotenv-expand": "^5.1.0", "js-yaml": "^4.1.0", @@ -5613,6 +6874,8 @@ "version": "2.3.7", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "peer": true, "requires": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -5623,6 +6886,16 @@ "util-deprecate": "~1.0.1" } }, + "readdir-glob": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/readdir-glob/-/readdir-glob-1.1.3.tgz", + "integrity": "sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==", + "dev": true, + "peer": true, + "requires": { + "minimatch": "^5.1.0" + } + }, "request": { "version": "2.88.2", "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", @@ -5662,14 +6935,11 @@ "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==", "dev": true }, - "rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } + "retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", + "dev": true }, "roarr": { "version": "2.15.4", @@ -5692,9 +6962,9 @@ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" }, "safe-stable-stringify": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-1.1.1.tgz", - "integrity": "sha512-ERq4hUjKDbJfE4+XtZLFPCDi8Vb1JqaxAPTxWFLBx8XcAlf9Bda/ZJdVezs/NAfsMQScyIlUMx+Yeu7P7rx5jw==" + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.4.3.tgz", + "integrity": "sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g==" }, "safer-buffer": { "version": "2.1.2", @@ -5711,9 +6981,9 @@ } }, "sax": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.3.0.tgz", + "integrity": "sha512-0s+oAmw9zLl1V1cS9BtZN7JAd0cW5e0QH4W3LWEK6a4LaLEA2OTpGYWDY+6XasBLtz6wkm3u1xRw95mRuJ59WA==", "dev": true }, "semver": { @@ -5757,6 +7027,12 @@ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true }, + "signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true + }, "simple-swizzle": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", @@ -5766,20 +7042,12 @@ } }, "simple-update-notifier": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-1.1.0.tgz", - "integrity": "sha512-VpsrsJSUcJEseSbMHkrsrAVSdvVS5I96Qo1QAQ4FxQ9wXFcB+pjj7FB7/us9+GcgfW4ziHtYMc1J0PLczb55mg==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz", + "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==", "dev": true, "requires": { - "semver": "~7.0.0" - }, - "dependencies": { - "semver": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz", - "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==", - "dev": true - } + "semver": "^7.5.3" } }, "slice-ansi": { @@ -5790,13 +7058,6 @@ "ansi-styles": "^4.0.0", "astral-regex": "^2.0.0", "is-fullwidth-code-point": "^3.0.0" - }, - "dependencies": { - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" - } } }, "smart-buffer": { @@ -5809,7 +7070,7 @@ "sort-keys": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-1.1.2.tgz", - "integrity": "sha1-RBttTTRnmPG05J6JIK37oOVD+a0=", + "integrity": "sha512-vzn8aSqKgytVik0iwdBEi+zevbTYZogewTUM6dtpmGwEcdzbub/TX4bCzRhebDCRC3QzXgJsLRKB2V/Oof7HXg==", "requires": { "is-plain-obj": "^1.0.0" } @@ -5817,7 +7078,7 @@ "sort-keys-length": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/sort-keys-length/-/sort-keys-length-1.0.1.tgz", - "integrity": "sha1-nLb09OnkgVWmqgZx7dM2/xR5oYg=", + "integrity": "sha512-GRbEOUqCxemTAk/b32F2xa8wDTs+Z1QHOkbhJDQTvv/6G3ZkbJ+frYWsTcc7cBB3Fu4wy4XlLCuNtJuMn7Gsvw==", "requires": { "sort-keys": "^1.0.0" } @@ -5888,18 +7149,17 @@ "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" - }, - "dependencies": { - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" - } + } + }, + "string-width-cjs": { + "version": "npm:string-width@4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" } }, "strip-ansi": { @@ -5910,6 +7170,15 @@ "ansi-regex": "^5.0.1" } }, + "strip-ansi-cjs": { + "version": "npm:strip-ansi@6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + }, "sumchecker": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/sumchecker/-/sumchecker-3.0.1.tgz", @@ -5929,19 +7198,47 @@ } }, "tar": { - "version": "6.1.13", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.13.tgz", - "integrity": "sha512-jdIBIN6LTIe2jqzay/2vtYLlBHa3JF42ot3h1dW8Q0PaAG4v8rm0cvpVePtau5C6OKXGGcgO9q2AMNSWxiLqKw==", + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", + "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", "dev": true, "requires": { "chownr": "^2.0.0", "fs-minipass": "^2.0.0", - "minipass": "^4.0.0", + "minipass": "^5.0.0", "minizlib": "^2.1.1", "mkdirp": "^1.0.3", "yallist": "^4.0.0" } }, + "tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "dev": true, + "peer": true, + "requires": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "peer": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } + } + }, "temp-file": { "version": "3.4.0", "resolved": "https://registry.npmjs.org/temp-file/-/temp-file-3.4.0.tgz", @@ -5974,9 +7271,9 @@ } }, "universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", "dev": true } } @@ -6003,13 +7300,10 @@ } }, "tmp": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", - "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", - "dev": true, - "requires": { - "rimraf": "^3.0.0" - } + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.3.tgz", + "integrity": "sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==", + "dev": true }, "tmp-promise": { "version": "3.0.3", @@ -6030,9 +7324,9 @@ } }, "triple-beam": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.3.0.tgz", - "integrity": "sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw==" + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.4.1.tgz", + "integrity": "sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==" }, "truncate-utf8-bytes": { "version": "1.0.2", @@ -6063,6 +7357,18 @@ "dev": true, "optional": true }, + "typescript": { + "version": "5.4.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", + "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", + "dev": true + }, + "undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "devOptional": true + }, "universalify": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", @@ -6087,9 +7393,9 @@ } }, "utf8-byte-length": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/utf8-byte-length/-/utf8-byte-length-1.0.4.tgz", - "integrity": "sha512-4+wkEYLBbWxqTahEsWrhxepcoVOJ+1z5PGIjPZxRkytcdSUaNjIjBM7Xn8E+pdSuV7SzvWovBFA54FO0JSoqhA==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/utf8-byte-length/-/utf8-byte-length-1.0.5.tgz", + "integrity": "sha512-Xn0w3MtiQ6zoz2vFyUVruaCL53O/DwUvkEeOvj+uulMm0BkUGYWmBYVyElqZaSLhY6ZD0ulfU3aBra2aVT4xfA==", "dev": true }, "util-deprecate": { @@ -6122,19 +7428,21 @@ } }, "winston": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/winston/-/winston-3.3.3.tgz", - "integrity": "sha512-oEXTISQnC8VlSAKf1KYSSd7J6IWuRPQqDdo8eoRNaYKLvwSb5+79Z3Yi1lrl6KDpU6/VWaxpakDAtb1oQ4n9aw==", + "version": "3.13.0", + "resolved": "https://registry.npmjs.org/winston/-/winston-3.13.0.tgz", + "integrity": "sha512-rwidmA1w3SE4j0E5MuIufFhyJPBDG7Nu71RkZor1p2+qHvJSZ9GYDA81AyleQcZbh/+V6HjeBdfnTZJm9rSeQQ==", "requires": { + "@colors/colors": "^1.6.0", "@dabh/diagnostics": "^2.0.2", - "async": "^3.1.0", + "async": "^3.2.3", "is-stream": "^2.0.0", - "logform": "^2.2.0", + "logform": "^2.4.0", "one-time": "^1.0.0", "readable-stream": "^3.4.0", + "safe-stable-stringify": "^2.3.1", "stack-trace": "0.0.x", "triple-beam": "^1.3.0", - "winston-transport": "^4.4.0" + "winston-transport": "^4.7.0" }, "dependencies": { "readable-stream": { @@ -6150,12 +7458,25 @@ } }, "winston-transport": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.4.0.tgz", - "integrity": "sha512-Lc7/p3GtqtqPBYYtS6KCN3c77/2QCev51DvcJKbkFPQNoj1sinkGwLGFDxkXY9J6p9+EPnYs+D90uwbnaiURTw==", + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.7.0.tgz", + "integrity": "sha512-ajBj65K5I7denzer2IYW6+2bNIVqLGDHqDw3Ow8Ohh+vdW+rv4MZ6eiDvHoKhfJFZ2auyN8byXieDDJ96ViONg==", "requires": { - "readable-stream": "^2.3.7", - "triple-beam": "^1.2.0" + "logform": "^2.3.2", + "readable-stream": "^3.6.0", + "triple-beam": "^1.3.0" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } } }, "wrap-ansi": { @@ -6169,6 +7490,17 @@ "strip-ansi": "^6.0.0" } }, + "wrap-ansi-cjs": { + "version": "npm:wrap-ansi@7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + }, "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", @@ -6221,6 +7553,51 @@ "buffer-crc32": "~0.2.3", "fd-slicer": "~1.1.0" } + }, + "zip-stream": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-4.1.1.tgz", + "integrity": "sha512-9qv4rlDiopXg4E69k+vMHjNN63YFMe9sZMrdlvKnCjlCRWeCBswPPMPUfx+ipsAWq1LXHe70RcbaHdJJpS6hyQ==", + "dev": true, + "peer": true, + "requires": { + "archiver-utils": "^3.0.4", + "compress-commons": "^4.1.2", + "readable-stream": "^3.6.0" + }, + "dependencies": { + "archiver-utils": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-3.0.4.tgz", + "integrity": "sha512-KVgf4XQVrTjhyWmx6cte4RxonPLR9onExufI1jhvw/MQ4BB6IsZD5gT8Lq+u/+pRkWna/6JoHpiQioaqFP5Rzw==", + "dev": true, + "peer": true, + "requires": { + "glob": "^7.2.3", + "graceful-fs": "^4.2.0", + "lazystream": "^1.0.0", + "lodash.defaults": "^4.2.0", + "lodash.difference": "^4.5.0", + "lodash.flatten": "^4.4.0", + "lodash.isplainobject": "^4.0.6", + "lodash.union": "^4.6.0", + "normalize-path": "^3.0.0", + "readable-stream": "^3.6.0" + } + }, + "readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "peer": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } + } } } } diff --git a/pyinstaller/electron/package.json b/pyinstaller/electron/package.json index 859ababcd1..e99df4a78d 100644 --- a/pyinstaller/electron/package.json +++ b/pyinstaller/electron/package.json @@ -1,7 +1,7 @@ { "name": "Specter", "description": "Specter Desktop Electron application", - "version": "v0.0.0", + "version": "v2.0.4-pre9", "main": "main.js", "scripts": { "start": "export NODE_ENV=development && cp -R ../../src/cryptoadvance/specter/static/fonts ../../src/cryptoadvance/specter/static/output.css ../../src/cryptoadvance/specter/static/typography.css . && electron .", @@ -20,8 +20,8 @@ "author": "Specter", "license": "MIT", "devDependencies": { - "electron": "^22.1.0", - "electron-builder": "^23.3.1" + "electron": "^30.0.6", + "electron-builder": "^24.13.3" }, "build": { "productName": "Specter", @@ -30,9 +30,17 @@ "category": "public.app-category.utilities", "identity": "Kim Neunert (FWV59JHV83)", "entitlements": "./build/entitlements.mac.plist", - "entitlementsInherit": "./build/entitlements.mac.plist", "hardenedRuntime": true, - "icon": "./icons/icon.icns" + "icon": "./icons/icon.icns", + "provisioningProfile": "/Users/kim/Specter_Desktop_vanilla.provisionprofile", + "target": [ + { + "target": "dmg", + "arch": [ + "universal" + ] + } + ] }, "win": { "icon": "./icons/icon.ico" @@ -45,12 +53,12 @@ } }, "dependencies": { - "electron-context-menu": "^2.3.0", + "electron-context-menu": "^3.6.1", "electron-default-menu": "^1.0.2", - "electron-progressbar": "^2.0.1", + "electron-progressbar": "^2.2.1", "extract-zip": "^2.0.1", "read-last-lines": "^1.8.0", "request": "^2.88.2", - "winston": "^3.3.3" + "winston": "^3.13.0" } } \ No newline at end of file diff --git a/pyinstaller/electron/renderer.js b/pyinstaller/electron/renderer.js deleted file mode 100644 index d3bdade6d2..0000000000 --- a/pyinstaller/electron/renderer.js +++ /dev/null @@ -1,6 +0,0 @@ -// This file is required by the index.html file and will -// be executed in the renderer process for that window. -// No Node.js APIs are available in this process because -// `nodeIntegration` is turned off. Use `preload.js` to -// selectively enable features needed in the rendering -// process. diff --git a/pyinstaller/electron/set-version.js b/pyinstaller/electron/set-version.js index 16d8642709..8b9d9f8fa8 100644 --- a/pyinstaller/electron/set-version.js +++ b/pyinstaller/electron/set-version.js @@ -1,25 +1,60 @@ -const fs = require('fs') -const crypto = require('crypto') -const version = process.argv[2] +const fs = require('fs'); +const crypto = require('crypto'); +const versionDataFile = './version-data.json'; async function setVersion() { - let package = require('./package.json') - package.version = version - fs.writeFileSync('./package.json', JSON.stringify(package, undefined, 2)) - - if (process.argv[3]) { - let versionData = { - version, - sha256: (await createHashFromFile(process.argv[3])) - } - - fs.writeFileSync('./version-data.json', JSON.stringify(versionData, undefined, 2)) - } + const version = process.argv[2]; + const file = process.argv[3]; + const arch = process.argv[4] || process.arch; + + // Set version in package.json + let packageJson = require('./package.json'); + packageJson.version = version; + fs.writeFileSync('./package.json', JSON.stringify(packageJson, undefined, 2)); + + // Set version in version-data.json + if (version && file) { + let versionData; + try { + versionData = require(versionDataFile); + + if (versionData.version != version) { + console.log(`Version mismatch. Deleting ${versionDataFile} and creating anew.`); + fs.unlinkSync(versionDataFile); // Delete the existing version-data.json file + versionData = createNewVersionData(version); // Create new version data object + } + + } catch (error) { + console.log(`No ${versionDataFile} found. Creating anew.`); + versionData = createNewVersionData(version); + } + // Compute SHA256 hash of the provided file + versionData.sha256[arch] = await createHashFromFile(file); + // Write new version data to file + fs.writeFileSync(versionDataFile, JSON.stringify(versionData, undefined, 2)); + console.log("version-data.js: ") + console.log("----------------------------------------------------------") + console.log(versionData); + console.log("----------------------------------------------------------") + } else if (arch || file) { + throw new Error("Declare both arch and file or none."); + } +} + +function createNewVersionData(version) { + // Return a new version data object + return { + version, + sha256: {} + }; } -const createHashFromFile = filePath => new Promise(resolve => { - const hash = crypto.createHash('sha256'); - fs.createReadStream(filePath).on('data', data => hash.update(data)).on('end', () => resolve(hash.digest('hex'))); +const createHashFromFile = filePath => new Promise((resolve, reject) => { + const hash = crypto.createHash('sha256'); + fs.createReadStream(filePath) + .on('data', data => hash.update(data)) + .on('end', () => resolve(hash.digest('hex'))) + .on('error', reject); }); -setVersion() +setVersion().catch(console.error); \ No newline at end of file diff --git a/pyinstaller/electron/settings.html b/pyinstaller/electron/settings.html index a3681e7a75..5dd7700151 100644 --- a/pyinstaller/electron/settings.html +++ b/pyinstaller/electron/settings.html @@ -115,10 +115,11 @@

Use a custom specterd file

const fs = require("fs"); const path = require("path"); const { ipcRenderer } = require("electron"); - const helpers = require("./helpers"); + const helpers = require("./src/helpers"); + const config = require("./src/config"); const getFileHash = helpers.getFileHash; - const getAppSettings = helpers.getAppSettings; - const appSettingsPath = helpers.appSettingsPath; + const getAppSettings = config.getAppSettings; + const appSettingsPath = config.appSettingsPath; const specterdDirPath = helpers.specterdDirPath; function toggleAdvanced() { diff --git a/pyinstaller/electron/src/config.js b/pyinstaller/electron/src/config.js new file mode 100644 index 0000000000..3e87e37066 --- /dev/null +++ b/pyinstaller/electron/src/config.js @@ -0,0 +1,110 @@ +const os = require('os') +const path = require('path') +const fs = require('fs') + + +const downloadloc = require('../downloadloc'); + + + +let platformName = '' +switch (process.platform) { + case 'darwin': + platformName = 'osx' + break + case 'win32': + platformName = 'win64' + break + case 'linux': + platformName = 'x86_64-linux-gnu' + break + default: + throw `Unknown platformName ${platformName}` +} +const appName = downloadloc.appName() +const appNameLower = appName.toLowerCase() +const isDev = process.env.NODE_ENV === "development" +const unresolvedDevFolder = process.env.SPECTER_DATA_FOLDER || "~/.specter_dev" +const devFolder = unresolvedDevFolder.replace(/^~/, os.homedir()); +const prodFolder = path.resolve(os.homedir(), `.${appNameLower}`) +let appSettingsPath +let appSettings +let specterdDirPath +let specterAppLogPath +let versionDataPath +let versionData + +function getAppSettings() { + let defaultSettings = { + mode: 'specterd', + specterURL: 'http://localhost:25441', + basicAuth: false, + basicAuthUser: '', + basicAuthPass: '', + tor: false, + proxyURL: "socks5://127.0.0.1:9050", + specterdVersion: (versionData && versionData.version !== undefined) ? versionData.version : 'unknown', + specterdHash: (versionData && versionData.sha256 !== undefined) ? versionData.sha256[process.arch] : 'unknown', + specterdCLIArgs: "", + versionInitialized: false + } + + try { + if (!fs.existsSync(appSettingsPath)){ + fs.mkdirSync(path.resolve(appSettingsPath, '..'), { recursive: true }); + } + fs.writeFileSync(appSettingsPath, JSON.stringify(defaultSettings), { flag: 'wx' }); + } catch (error) { + if (error.toString().startsWith("Error: EEXIST: file already exists,")) { + //ignore + } else { + throw error + } + } + + // Make sure to add missing settings in case the format changed or new settings were added + let appSettings = require(appSettingsPath) + for (let key of Object.keys(defaultSettings)) { + if (!appSettings.hasOwnProperty(key)) { + appSettings[key] = defaultSettings[key] + } + } + + return appSettings +} + +if (isDev) { + versionDataPath = `${devFolder}/version-data.json` + +} else { + versionDataPath = `../version-data.json` +} +versionData = require(versionDataPath) + + +if (isDev) { + appSettingsPath = `${devFolder}/app_settings.json` + specterdDirPath = `${devFolder}/specterd-binaries` + specterAppLogPath = `${devFolder}/specterApp.log` +} +else { + appSettingsPath = `${prodFolder}/app_settings.json` + specterdDirPath = `${prodFolder}/specterd-binaries` + specterAppLogPath = `${prodFolder}/specterApp.log` +} +appSettings = getAppSettings() + +module.exports = { + platformName, + appSettings, + appName, + appNameLower, + appSettingsPath, + specterdDirPath, + specterAppLogPath, + versionDataPath, + versionData, + getAppSettings, + isDev: isDev, + devFolder, +} \ No newline at end of file diff --git a/pyinstaller/electron/src/download.js b/pyinstaller/electron/src/download.js new file mode 100644 index 0000000000..31dbf679af --- /dev/null +++ b/pyinstaller/electron/src/download.js @@ -0,0 +1,158 @@ +const request = require('request') +const fs = require('fs') +const { app, Menu } = require('electron') +const extract = require('extract-zip') +const { getDownloadLocation } = require('../downloadloc.js') +const { appName, appSettings, platformName, appNameLower, versionDataPath } = require('./config.js') +const { isMac, getFileHash } = require('./helpers.js') +const { logger } = require('./logging.js') +const ProgressBar = require('electron-progressbar') +const { updateSpecterdStatus, updatingLoaderMsg, createProgressBar } = require('./uiHelpers.js') +const { startSpecterd } = require('./specterd.js') + +let progressBar + +// The standard quit item cannot be replaced / modified and it is not triggering the +// before-quit event on MacOS if a child window is open +const dockMenuWithforceQuit = Menu.buildFromTemplate([ + { + label: 'Force Quit during download', + click: () => { + // If the progress bar exists, close it + if (progressBar) { + progressBar.close() + } + // Quit the app + app.quit() + }, + }, +]) + +function downloadSpecterd(specterdPath) { + updatingLoaderMsg(`Starting download`) + updateSpecterdStatus(`Downloading the ${appName} binary...`) + // Some logging + logger.info('Using version ' + appSettings.specterdVersion) + logger.info('Using platformName ' + platformName) + download_location = getDownloadLocation(appSettings.specterdVersion, platformName) + logger.info('Downloading from ' + download_location) + download(download_location, specterdPath + '.zip', function (errored) { + if (errored == true) { + updatingLoaderMsg( + `Downloading the ${appNameLower} binary from GitHub failed, could not reach the server or the file wasn't found.` + ) + updateSpecterdStatus(`Downloading ${appNameLower}d failed...`) + return + } + updatingLoaderMsg('Download completed. Unpacking files...') + logger.info('Extracting ' + specterdPath) + + extract(specterdPath + '.zip', { dir: specterdPath + '-dir' }).then(function () { + let extraPath = '' + switch (process.platform) { + case 'darwin': + extraPath = appNameLower + 'd' + break + case 'win32': + extraPath = appNameLower + 'd.exe' + break + case 'linux': + extraPath = appNameLower + 'd' + } + var oldPath = specterdPath + `-dir/${extraPath}` + var newPath = specterdPath + (platformName == 'win64' ? '.exe' : '') + + fs.renameSync(oldPath, newPath) + fs.unlinkSync(specterdPath + '.zip') + fs.rmdirSync(specterdPath + '-dir', { recursive: true }) + getFileHash(specterdPath + (platformName == 'win64' ? '.exe' : ''), function (specterdHash) { + if (appSettings.specterdHash.toLowerCase() === specterdHash || appSettings.specterdHash == '') { + startSpecterd(specterdPath) + } else { + updatingLoaderMsg('Specterd version could not be validated.') + logger.error(`hash of downloaded file: ${specterdHash}`) + logger.error(`Expected hash: ${appSettings.specterdHash} from ${versionDataPath}`) + updateSpecterdStatus('Failed to launch specterd...') + } + }) + }) + }) +} + +// Download function with progress bar +const download = (uri, filename, callback) => { + // HEAD request first + request.head(uri, (err, res, body) => { + if (res.statusCode != 404) { + let receivedBytes = 0 + const totalBytes = res.headers['content-length'] + logger.info(`Total size to download: ${totalBytes}`) + progressBar = createProgressBar(totalBytes) + // Add Force Quit item during download for MacOS dock + if (isMac) { + app.dock.setMenu(dockMenuWithforceQuit) + } + + progressBar.on('completed', () => { + progressBar.close() + // Remove the Force Quit dock item again for Mac + if (isMac) { + const updatedDockMenu = Menu.buildFromTemplate( + dockMenuWithforceQuit.items.filter((item) => item.label !== 'Force Quit during download') + ) + app.dock.setMenu(updatedDockMenu) + } + }) + + progressBar.on('aborted', () => { + logger.warn('Download was aborted before it could finish.') + }) + // Loggin the download progress + let lastLogTime = 0 + const logInterval = 5000 // log every 5 seconds + progressBar.on('progress', () => { + const currentTime = Date.now() + if (currentTime - lastLogTime >= logInterval) { + lastLogTime = currentTime + logger.info(`Download status: ${((receivedBytes / totalBytes) * 100).toFixed(0)}%`) + } + }) + // GET request + request(uri) + .on('data', (chunk) => { + receivedBytes += chunk.length + if (progressBar) { + progressBar.value = receivedBytes + } + }) + .pipe(fs.createWriteStream(filename)) + .on('close', callback) + } + // If the download link was not found, call callback (updatingLoaderMsg with error feedback) + else { + logger.error(`Error while trying to download specterd: ${err}`) + try { + callback(true) + } catch (error) { + logger.error(error) + throw error + } + } + }) +} + +const destroyProgressbar = () => { + if (progressBar) { + // You can only destroy the progress bar if it hadn't been closed before + if (progressBar.browserWindow) { + progressBar.destroy() + } + progressBar = null + } +} + +module.exports = { + downloadSpecterd: downloadSpecterd, + download: download, + destroyProgressbar, +} diff --git a/pyinstaller/electron/src/helpers.js b/pyinstaller/electron/src/helpers.js new file mode 100644 index 0000000000..ec32fc300a --- /dev/null +++ b/pyinstaller/electron/src/helpers.js @@ -0,0 +1,43 @@ +const { Menu } = require('electron') +const fs = require('fs') +const path = require('path') +const crypto = require('crypto') +const readLastLines = require('read-last-lines') +const isMac = process.platform === 'darwin' + +const { versionData, specterAppLogPath } = require('./config.js') +const { logger } = require('./logging.js') + +// Use different version-data.jsons + +// Should look like this: +// { +// "version": "v2.0.0-pre32", +// "sha256": "aa049abf3e75199bad26fbded08ee5911ad48e325b42c43ec195136bd0736785" +// } + +function getFileHash(filename, callback) { + let shasum = crypto.createHash('sha256'), + // Updating shasum with file content + s = fs.ReadStream(filename) + s.on('data', function (data) { + shasum.update(data) + }) + // making digest + s.on('end', function () { + var hash = shasum.digest('hex') + callback(hash) + }) +} + +function getSpecterAppLogs(callback) { + readLastLines.read(specterAppLogPath, 700).then(callback) +} + +module.exports = { + getFileHash, + getSpecterAppLogs, + versionData, + isMac, + isMac, +} diff --git a/pyinstaller/electron/src/logging.js b/pyinstaller/electron/src/logging.js new file mode 100644 index 0000000000..0715a93b25 --- /dev/null +++ b/pyinstaller/electron/src/logging.js @@ -0,0 +1,22 @@ +const { specterAppLogPath } = require('./config.js') + +// Logging +const { transports, format, createLogger } = require('winston') +const combinedLog = new transports.File({ filename: specterAppLogPath }) +const winstonOptions = { + exitOnError: false, + format: format.combine( + format.timestamp(), + format.json(), + format.printf((info) => { + return `${info.timestamp} [${info.level}] : ${info.message}` + }) + ), + transports: [new transports.Console({ json: false }), combinedLog], + exceptionHandlers: [combinedLog], +} +const logger = createLogger(winstonOptions) + +module.exports = { + logger: logger, +} \ No newline at end of file diff --git a/pyinstaller/electron/src/output.css b/pyinstaller/electron/src/output.css new file mode 100644 index 0000000000..9d32dfac49 --- /dev/null +++ b/pyinstaller/electron/src/output.css @@ -0,0 +1,2430 @@ +/* +! tailwindcss v3.2.4 | MIT License | https://tailwindcss.com +*/ + +/* +1. Prevent padding and border from affecting element width. (https://github.com/mozdevs/cssremedy/issues/4) +2. Allow adding a border to an element by just adding a border-width. (https://github.com/tailwindcss/tailwindcss/pull/116) +*/ + +*, +::before, +::after { + box-sizing: border-box; + /* 1 */ + border-width: 0; + /* 2 */ + border-style: solid; + /* 2 */ + border-color: #e5e7eb; + /* 2 */ +} + +::before, +::after { + --tw-content: ''; +} + +/* +1. Use a consistent sensible line-height in all browsers. +2. Prevent adjustments of font size after orientation changes in iOS. +3. Use a more readable tab size. +4. Use the user's configured `sans` font-family by default. +5. Use the user's configured `sans` font-feature-settings by default. +*/ + +html { + line-height: 1.5; + /* 1 */ + -webkit-text-size-adjust: 100%; + /* 2 */ + -moz-tab-size: 4; + /* 3 */ + -o-tab-size: 4; + tab-size: 4; + /* 3 */ + font-family: Social, sans-serif; + /* 4 */ + font-feature-settings: normal; + /* 5 */ +} + +/* +1. Remove the margin in all browsers. +2. Inherit line-height from `html` so users can set them as a class directly on the `html` element. +*/ + +body { + margin: 0; + /* 1 */ + line-height: inherit; + /* 2 */ +} + +/* +1. Add the correct height in Firefox. +2. Correct the inheritance of border color in Firefox. (https://bugzilla.mozilla.org/show_bug.cgi?id=190655) +3. Ensure horizontal rules are visible by default. +*/ + +hr { + height: 0; + /* 1 */ + color: inherit; + /* 2 */ + border-top-width: 1px; + /* 3 */ +} + +/* +Add the correct text decoration in Chrome, Edge, and Safari. +*/ + +abbr:where([title]) { + -webkit-text-decoration: underline dotted; + text-decoration: underline dotted; +} + +/* +Remove the default font size and weight for headings. +*/ + +h1, +h2, +h3, +h4, +h5, +h6 { + font-size: inherit; + font-weight: inherit; +} + +/* +Reset links to optimize for opt-in styling instead of opt-out. +*/ + +a { + color: inherit; + text-decoration: inherit; +} + +/* +Add the correct font weight in Edge and Safari. +*/ + +b, +strong { + font-weight: bolder; +} + +/* +1. Use the user's configured `mono` font family by default. +2. Correct the odd `em` font sizing in all browsers. +*/ + +code, +kbd, +samp, +pre { + font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; + /* 1 */ + font-size: 1em; + /* 2 */ +} + +/* +Add the correct font size in all browsers. +*/ + +small { + font-size: 80%; +} + +/* +Prevent `sub` and `sup` elements from affecting the line height in all browsers. +*/ + +sub, +sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; +} + +sub { + bottom: -0.25em; +} + +sup { + top: -0.5em; +} + +/* +1. Remove text indentation from table contents in Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=999088, https://bugs.webkit.org/show_bug.cgi?id=201297) +2. Correct table border color inheritance in all Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=935729, https://bugs.webkit.org/show_bug.cgi?id=195016) +3. Remove gaps between table borders by default. +*/ + +table { + text-indent: 0; + /* 1 */ + border-color: inherit; + /* 2 */ + border-collapse: collapse; + /* 3 */ +} + +/* +1. Change the font styles in all browsers. +2. Remove the margin in Firefox and Safari. +3. Remove default padding in all browsers. +*/ + +button, +input, +optgroup, +select, +textarea { + font-family: inherit; + /* 1 */ + font-size: 100%; + /* 1 */ + font-weight: inherit; + /* 1 */ + line-height: inherit; + /* 1 */ + color: inherit; + /* 1 */ + margin: 0; + /* 2 */ + padding: 0; + /* 3 */ +} + +/* +Remove the inheritance of text transform in Edge and Firefox. +*/ + +button, +select { + text-transform: none; +} + +/* +1. Correct the inability to style clickable types in iOS and Safari. +2. Remove default button styles. +*/ + +button, +[type='button'], +[type='reset'], +[type='submit'] { + -webkit-appearance: button; + /* 1 */ + background-color: transparent; + /* 2 */ + background-image: none; + /* 2 */ +} + +/* +Use the modern Firefox focus style for all focusable elements. +*/ + +:-moz-focusring { + outline: auto; +} + +/* +Remove the additional `:invalid` styles in Firefox. (https://github.com/mozilla/gecko-dev/blob/2f9eacd9d3d995c937b4251a5557d95d494c9be1/layout/style/res/forms.css#L728-L737) +*/ + +:-moz-ui-invalid { + box-shadow: none; +} + +/* +Add the correct vertical alignment in Chrome and Firefox. +*/ + +progress { + vertical-align: baseline; +} + +/* +Correct the cursor style of increment and decrement buttons in Safari. +*/ + +::-webkit-inner-spin-button, +::-webkit-outer-spin-button { + height: auto; +} + +/* +1. Correct the odd appearance in Chrome and Safari. +2. Correct the outline style in Safari. +*/ + +[type='search'] { + -webkit-appearance: textfield; + /* 1 */ + outline-offset: -2px; + /* 2 */ +} + +/* +Remove the inner padding in Chrome and Safari on macOS. +*/ + +::-webkit-search-decoration { + -webkit-appearance: none; +} + +/* +1. Correct the inability to style clickable types in iOS and Safari. +2. Change font properties to `inherit` in Safari. +*/ + +::-webkit-file-upload-button { + -webkit-appearance: button; + /* 1 */ + font: inherit; + /* 2 */ +} + +/* +Add the correct display in Chrome and Safari. +*/ + +summary { + display: list-item; +} + +/* +Removes the default spacing and border for appropriate elements. +*/ + +blockquote, +dl, +dd, +h1, +h2, +h3, +h4, +h5, +h6, +hr, +figure, +p, +pre { + margin: 0; +} + +fieldset { + margin: 0; + padding: 0; +} + +legend { + padding: 0; +} + +ol, +ul, +menu { + list-style: none; + margin: 0; + padding: 0; +} + +/* +Prevent resizing textareas horizontally by default. +*/ + +textarea { + resize: vertical; +} + +/* +1. Reset the default placeholder opacity in Firefox. (https://github.com/tailwindlabs/tailwindcss/issues/3300) +2. Set the default placeholder color to the user's configured gray 400 color. +*/ + +input::-moz-placeholder, textarea::-moz-placeholder { + opacity: 1; + /* 1 */ + color: #9ca3af; + /* 2 */ +} + +input::placeholder, +textarea::placeholder { + opacity: 1; + /* 1 */ + color: #9ca3af; + /* 2 */ +} + +/* +Set the default cursor for buttons. +*/ + +button, +[role="button"] { + cursor: pointer; +} + +/* +Make sure disabled buttons don't get the pointer cursor. +*/ + +:disabled { + cursor: default; +} + +/* +1. Make replaced elements `display: block` by default. (https://github.com/mozdevs/cssremedy/issues/14) +2. Add `vertical-align: middle` to align replaced elements more sensibly by default. (https://github.com/jensimmons/cssremedy/issues/14#issuecomment-634934210) + This can trigger a poorly considered lint error in some tools but is included by design. +*/ + +img, +svg, +video, +canvas, +audio, +iframe, +embed, +object { + display: block; + /* 1 */ + vertical-align: middle; + /* 2 */ +} + +/* +Constrain images and videos to the parent width and preserve their intrinsic aspect ratio. (https://github.com/mozdevs/cssremedy/issues/14) +*/ + +img, +video { + max-width: 100%; + height: auto; +} + +/* Make elements with the HTML hidden attribute stay hidden by default */ + +[hidden] { + display: none; +} + +/* + * + * General HTML Tags + * + */ + +h1 { + margin-bottom: 0.5rem; + font-size: 2.25rem; + line-height: 2.5rem; + font-weight: 500; +} + +h2 { + margin-bottom: 0.5rem; + font-size: 1.5rem; + line-height: 2rem; + font-weight: 500; +} + +h3 { + margin-bottom: 0.5rem; + font-size: 1.25rem; + line-height: 1.75rem; + font-weight: 500; +} + +p { + margin-top: 0.25rem; + margin-bottom: 0.25rem; + max-width: 65ch; + font-size: 1.125rem; + line-height: 1.75rem; + --tw-text-opacity: 1; + color: rgb(207 207 208 / var(--tw-text-opacity)); +} + +a { + text-decoration-line: none !important; +} + +p a { + --tw-text-opacity: 1; + color: rgb(41 151 255 / var(--tw-text-opacity)); +} + +p a:hover { + opacity: 0.8; +} + +table { + min-width: 100%; + --tw-bg-opacity: 1; + background-color: rgb(20 21 26 / var(--tw-bg-opacity)); +} + +tbody { +} + +tr { + border-bottom-width: 1px; + --tw-border-opacity: 1; + border-color: rgb(27 28 33 / var(--tw-border-opacity)); +} + +tbody tr:last-child { + border-bottom-width: 0px; +} + +td, + th { + white-space: nowrap; + border-right-width: 1px; + --tw-border-opacity: 1; + border-color: rgb(27 28 33 / var(--tw-border-opacity)); + padding-left: 0.75rem; + padding-right: 0.75rem; + padding-top: 0.375rem; + padding-bottom: 0.375rem; + text-align: left; + --tw-text-opacity: 1; + color: rgb(255 255 255 / var(--tw-text-opacity)); +} + +td:last-child, + th:last-child { + border-right-width: 0px; +} + +td, + th { + /* first:pl-4 last:pr-4 */ +} + +.table-key { + width: 20rem; + overflow: scroll; +} + +th { + word-break: keep-all; + font-weight: 400; + --tw-text-opacity: 1; + color: rgb(159 159 161 / var(--tw-text-opacity)); +} + +ol { + margin-bottom: 2rem; + list-style-position: inside; + list-style-type: decimal; + font-size: 1.125rem; + line-height: 1.75rem; +} + +ol li { + margin-top: 0.5rem; + margin-bottom: 0.5rem; +} + +/* + * + * + * Form + * + * + */ + +form h3 { + font-size: 1.25rem; + line-height: 1.75rem; + font-weight: 400; +} + +form section { + margin-top: 2rem; + margin-bottom: 1rem; +} + +input[type="text"], + input[type="url"], + input[type="password"], + input[type="number"], + textarea { + height: 3rem; + width: 100%; + border-radius: 0.5rem; + border-width: 2px; + --tw-border-opacity: 1; + border-color: rgb(27 28 33 / var(--tw-border-opacity)); + --tw-bg-opacity: 1; + background-color: rgb(20 21 26 / var(--tw-bg-opacity)); + padding-left: 0.5rem; + padding-right: 0.5rem; + outline: 2px solid transparent; + outline-offset: 2px; + transition-duration: 200ms; + transition-timing-function: cubic-bezier(0, 0, 0.2, 1); +} + +input[type="text"]:focus, + input[type="url"]:focus, + input[type="password"]:focus, + input[type="number"]:focus, + textarea:focus { + --tw-border-opacity: 1; + border-color: rgb(46 92 255 / var(--tw-border-opacity)); +} + +textarea { + min-height: 80px; + padding-top: 0.5rem; +} + +input[type="checkbox"] { + margin-right: 0.5rem; + height: 1rem; + width: 1rem; +} + +.floating-wrapper { + position: relative; +} + +.floating-input { + padding-top: 1rem; +} + +.floating-input:-moz-placeholder-shown { + padding-top: 0px; +} + +.floating-input:placeholder-shown { + padding-top: 0px; +} + +.floating-label { + pointer-events: none; + position: absolute; + top: 0.5rem; + left: 0.25rem; + z-index: 10; + transform-origin: 0; + --tw-translate-y: -0.375rem; + --tw-scale-x: .75; + --tw-scale-y: .75; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); + cursor: text; + -webkit-user-select: none; + -moz-user-select: none; + user-select: none; + padding-left: 0.5rem; + padding-right: 0.5rem; + opacity: 0.5; + transition-duration: 200ms; +} + +.peer:-moz-placeholder-shown ~ .floating-label { + top: 50%; + --tw-translate-y: -50%; + --tw-scale-x: 1; + --tw-scale-y: 1; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); +} + +.peer:placeholder-shown ~ .floating-label { + top: 50%; + --tw-translate-y: -50%; + --tw-scale-x: 1; + --tw-scale-y: 1; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); +} + +.peer:focus ~ .floating-label { + padding-left: 0.5rem; + padding-right: 0.5rem; +} + +.floating-info { + position: absolute; + right: 0.75rem; + top: 0.5rem; +} + +.checkbox-wrapper-inline { + position: relative; + display: flex; + cursor: pointer; + align-items: center; +} + +.checkbox-wrapper-inline > :not([hidden]) ~ :not([hidden]) { + --tw-space-x-reverse: 0; + margin-right: calc(0.25rem * var(--tw-space-x-reverse)); + margin-left: calc(0.25rem * calc(1 - var(--tw-space-x-reverse))); +} + +.checkbox-wrapper { + position: relative; + display: flex; + height: 3rem; + cursor: pointer; + align-items: center; +} + +.checkbox-wrapper > :not([hidden]) ~ :not([hidden]) { + --tw-space-x-reverse: 0; + margin-right: calc(0.5rem * var(--tw-space-x-reverse)); + margin-left: calc(0.5rem * calc(1 - var(--tw-space-x-reverse))); +} + +.checkbox-wrapper { + border-radius: 0.5rem; + border-width: 2px; + --tw-border-opacity: 1; + border-color: rgb(27 28 33 / var(--tw-border-opacity)); + --tw-bg-opacity: 1; + background-color: rgb(20 21 26 / var(--tw-bg-opacity)); + padding-left: 0.75rem; + padding-right: 0.75rem; +} + +.checkbox-input { + height: 1rem; + width: 1rem; +} + +.checkbox-input:disabled { + opacity: 0.2; +} + +.checkbox-label { + font-weight: 500; +} + +.peer:disabled ~ .checkbox-label { + opacity: 0.2; +} + +select { + height: 3rem; + width: 100%; + border-radius: 0.5rem; + border-width: 2px; + --tw-border-opacity: 1; + border-color: rgb(27 28 33 / var(--tw-border-opacity)); + --tw-bg-opacity: 1; + background-color: rgb(20 21 26 / var(--tw-bg-opacity)); + padding-left: 0.5rem; + padding-right: 0.5rem; + outline: 2px solid transparent; + outline-offset: 2px; +} + +/* + * + * Button + * + */ + +.button { + z-index: 10; + margin-bottom: 0.75rem; + display: flex; + height: 2.75rem; + width: calc(688px / 2); + cursor: pointer; + align-items: center; + justify-content: center; +} + +.button > :not([hidden]) ~ :not([hidden]) { + --tw-space-x-reverse: 0; + margin-right: calc(0.5rem * var(--tw-space-x-reverse)); + margin-left: calc(0.5rem * calc(1 - var(--tw-space-x-reverse))); +} + +.button { + border-radius: 0.75rem; + --tw-bg-opacity: 1; + background-color: rgb(44 45 49 / var(--tw-bg-opacity)); + padding-left: 2.5rem; + padding-right: 2.5rem; + font-size: 1.125rem; + line-height: 1.75rem; +} + +.button:hover { + opacity: 0.8; +} + +.tooltip:hover .tooltip__box { + z-index: 40; +} + +.button img { + height: 1.75rem; + width: 1.75rem; +} + +/* Smaller button, that also has darker background color as of now, naming should be changed!*/ + +.btn { + margin-right: 0.25rem; + margin-left: -2px; + word-break: keep-all; + border-radius: 9999px; + border-width: 2px; + --tw-border-opacity: 1; + border-color: rgb(20 21 26 / var(--tw-border-opacity)); + --tw-bg-opacity: 1; + background-color: rgb(20 21 26 / var(--tw-bg-opacity)); + padding-top: 2px; + padding-bottom: 2px; + padding-left: 0.75rem; + padding-right: 0.75rem; +} + +.btn:hover { + --tw-border-opacity: 1; + border-color: rgb(27 28 33 / var(--tw-border-opacity)); +} + +/* + * + * Selection Items + * + */ + +.selection { + display: flex; + aspect-ratio: 1 / 1; + cursor: pointer; + flex-direction: column; + align-items: center; + justify-content: space-between; + border-radius: 0.5rem; + border-width: 2px; + --tw-border-opacity: 1; + border-color: rgb(27 28 33 / var(--tw-border-opacity)); + --tw-bg-opacity: 1; + background-color: rgb(20 21 26 / var(--tw-bg-opacity)); + padding-bottom: 0.5rem; +} + +.selection:hover { + --tw-bg-opacity: 1; + background-color: rgb(27 28 33 / var(--tw-bg-opacity)); +} + +.selection p { + margin: 0px; + text-align: center; + font-size: 1rem; + line-height: 1.5rem; + --tw-text-opacity: 1; + color: rgb(255 255 255 / var(--tw-text-opacity)); +} + +.selection img { + margin-bottom: 1rem; + height: 40%; + width: 40%; +} + +.selection-button { + display: block; + height: 2.5rem; + width: 100%; + border-radius: 0.5rem; + border-width: 2px; + --tw-border-opacity: 1; + border-color: rgb(27 28 33 / var(--tw-border-opacity)); + --tw-bg-opacity: 1; + background-color: rgb(20 21 26 / var(--tw-bg-opacity)); + padding-top: 0.25rem; + padding-bottom: 0.25rem; + text-align: center; + font-size: 1.125rem; + line-height: 1.75rem; +} + +.selection-button:hover { + --tw-bg-opacity: 1; + background-color: rgb(27 28 33 / var(--tw-bg-opacity)); +} + +.animate-spin-slow { + animation: spin 1.5s linear infinite; +} + +*, ::before, ::after { + --tw-border-spacing-x: 0; + --tw-border-spacing-y: 0; + --tw-translate-x: 0; + --tw-translate-y: 0; + --tw-rotate: 0; + --tw-skew-x: 0; + --tw-skew-y: 0; + --tw-scale-x: 1; + --tw-scale-y: 1; + --tw-pan-x: ; + --tw-pan-y: ; + --tw-pinch-zoom: ; + --tw-scroll-snap-strictness: proximity; + --tw-ordinal: ; + --tw-slashed-zero: ; + --tw-numeric-figure: ; + --tw-numeric-spacing: ; + --tw-numeric-fraction: ; + --tw-ring-inset: ; + --tw-ring-offset-width: 0px; + --tw-ring-offset-color: #fff; + --tw-ring-color: rgb(59 130 246 / 0.5); + --tw-ring-offset-shadow: 0 0 #0000; + --tw-ring-shadow: 0 0 #0000; + --tw-shadow: 0 0 #0000; + --tw-shadow-colored: 0 0 #0000; + --tw-blur: ; + --tw-brightness: ; + --tw-contrast: ; + --tw-grayscale: ; + --tw-hue-rotate: ; + --tw-invert: ; + --tw-saturate: ; + --tw-sepia: ; + --tw-drop-shadow: ; + --tw-backdrop-blur: ; + --tw-backdrop-brightness: ; + --tw-backdrop-contrast: ; + --tw-backdrop-grayscale: ; + --tw-backdrop-hue-rotate: ; + --tw-backdrop-invert: ; + --tw-backdrop-opacity: ; + --tw-backdrop-saturate: ; + --tw-backdrop-sepia: ; +} + +::backdrop { + --tw-border-spacing-x: 0; + --tw-border-spacing-y: 0; + --tw-translate-x: 0; + --tw-translate-y: 0; + --tw-rotate: 0; + --tw-skew-x: 0; + --tw-skew-y: 0; + --tw-scale-x: 1; + --tw-scale-y: 1; + --tw-pan-x: ; + --tw-pan-y: ; + --tw-pinch-zoom: ; + --tw-scroll-snap-strictness: proximity; + --tw-ordinal: ; + --tw-slashed-zero: ; + --tw-numeric-figure: ; + --tw-numeric-spacing: ; + --tw-numeric-fraction: ; + --tw-ring-inset: ; + --tw-ring-offset-width: 0px; + --tw-ring-offset-color: #fff; + --tw-ring-color: rgb(59 130 246 / 0.5); + --tw-ring-offset-shadow: 0 0 #0000; + --tw-ring-shadow: 0 0 #0000; + --tw-shadow: 0 0 #0000; + --tw-shadow-colored: 0 0 #0000; + --tw-blur: ; + --tw-brightness: ; + --tw-contrast: ; + --tw-grayscale: ; + --tw-hue-rotate: ; + --tw-invert: ; + --tw-saturate: ; + --tw-sepia: ; + --tw-drop-shadow: ; + --tw-backdrop-blur: ; + --tw-backdrop-brightness: ; + --tw-backdrop-contrast: ; + --tw-backdrop-grayscale: ; + --tw-backdrop-hue-rotate: ; + --tw-backdrop-invert: ; + --tw-backdrop-opacity: ; + --tw-backdrop-saturate: ; + --tw-backdrop-sepia: ; +} + +.container { + width: 100%; +} + +@media (min-width: 640px) { + .container { + max-width: 640px; + } +} + +@media (min-width: 768px) { + .container { + max-width: 768px; + } +} + +@media (min-width: 1024px) { + .container { + max-width: 1024px; + } +} + +@media (min-width: 1280px) { + .container { + max-width: 1280px; + } +} + +@media (min-width: 1536px) { + .container { + max-width: 1536px; + } +} + +select { + -webkit-appearance: none; + -moz-appearance: none; + background-image: url("data:image/svg+xml;utf8,"); + background-repeat: no-repeat; + background-position: top 9px right 12px; + padding-right: 32px; +} + +input[type="number"]::-webkit-outer-spin-button, + input[type="number"]::-webkit-inner-spin-button, + input[type="number"] { + -webkit-appearance: none; + margin: 0; + -moz-appearance: textfield !important; +} + +.table-holder { + margin: 20px calc((100vw - 1080px) / -2) 0; + max-width: calc((100vw - 300px - 80px)); +} + +.table-holder::-webkit-scrollbar, + .table-key::-webkit-scrollbar { + display: none; +} + +.sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + border-width: 0; +} + +.pointer-events-none { + pointer-events: none; +} + +.visible { + visibility: visible; +} + +.invisible { + visibility: hidden; +} + +.static { + position: static; +} + +.fixed { + position: fixed; +} + +.absolute { + position: absolute; +} + +.relative { + position: relative; +} + +.bottom-0 { + bottom: 0px; +} + +.right-0 { + right: 0px; +} + +.right-2 { + right: 0.5rem; +} + +.top-2 { + top: 0.5rem; +} + +.right-0\.5 { + right: 0.125rem; +} + +.right-10 { + right: 2.5rem; +} + +.top-2\.5 { + top: 0.625rem; +} + +.top-\[45\%\] { + top: 45%; +} + +.top-0 { + top: 0px; +} + +.z-50 { + z-index: 50; +} + +.z-30 { + z-index: 30; +} + +.col-span-4 { + grid-column: span 4 / span 4; +} + +.m-1 { + margin: 0.25rem; +} + +.m-0 { + margin: 0px; +} + +.m-auto { + margin: auto; +} + +.m-3 { + margin: 0.75rem; +} + +.m-4 { + margin: 1rem; +} + +.my-2 { + margin-top: 0.5rem; + margin-bottom: 0.5rem; +} + +.my-10 { + margin-top: 2.5rem; + margin-bottom: 2.5rem; +} + +.my-3 { + margin-top: 0.75rem; + margin-bottom: 0.75rem; +} + +.-mx-4 { + margin-left: -1rem; + margin-right: -1rem; +} + +.my-\[2px\] { + margin-top: 2px; + margin-bottom: 2px; +} + +.mx-2 { + margin-left: 0.5rem; + margin-right: 0.5rem; +} + +.mx-4 { + margin-left: 1rem; + margin-right: 1rem; +} + +.my-1 { + margin-top: 0.25rem; + margin-bottom: 0.25rem; +} + +.my-5 { + margin-top: 1.25rem; + margin-bottom: 1.25rem; +} + +.mx-3 { + margin-left: 0.75rem; + margin-right: 0.75rem; +} + +.-mx-3 { + margin-left: -0.75rem; + margin-right: -0.75rem; +} + +.mt-20 { + margin-top: 5rem; +} + +.mt-5 { + margin-top: 1.25rem; +} + +.mt-3 { + margin-top: 0.75rem; +} + +.mt-8 { + margin-top: 2rem; +} + +.mb-3 { + margin-bottom: 0.75rem; +} + +.mb-2 { + margin-bottom: 0.5rem; +} + +.mb-0 { + margin-bottom: 0px; +} + +.-mb-px { + margin-bottom: -1px; +} + +.mr-2 { + margin-right: 0.5rem; +} + +.mb-5 { + margin-bottom: 1.25rem; +} + +.mt-10 { + margin-top: 2.5rem; +} + +.mb-8 { + margin-bottom: 2rem; +} + +.-mt-\[2px\] { + margin-top: -2px; +} + +.mr-1 { + margin-right: 0.25rem; +} + +.ml-4 { + margin-left: 1rem; +} + +.mt-2 { + margin-top: 0.5rem; +} + +.mr-4 { + margin-right: 1rem; +} + +.ml-6 { + margin-left: 1.5rem; +} + +.mb-4 { + margin-bottom: 1rem; +} + +.mr-3 { + margin-right: 0.75rem; +} + +.mt-4 { + margin-top: 1rem; +} + +.mb-1 { + margin-bottom: 0.25rem; +} + +.mt-1 { + margin-top: 0.25rem; +} + +.mr-0 { + margin-right: 0px; +} + +.ml-5 { + margin-left: 1.25rem; +} + +.ml-2 { + margin-left: 0.5rem; +} + +.mb-12 { + margin-bottom: 3rem; +} + +.mb-10 { + margin-bottom: 2.5rem; +} + +.mt-16 { + margin-top: 4rem; +} + +.mt-\[-1px\] { + margin-top: -1px; +} + +.block { + display: block; +} + +.inline-block { + display: inline-block; +} + +.inline { + display: inline; +} + +.flex { + display: flex; +} + +.inline-flex { + display: inline-flex; +} + +.table { + display: table; +} + +.table-row { + display: table-row; +} + +.grid { + display: grid; +} + +.contents { + display: contents; +} + +.hidden { + display: none; +} + +.h-screen { + height: 100vh; +} + +.h-16 { + height: 4rem; +} + +.h-full { + height: 100%; +} + +.h-8 { + height: 2rem; +} + +.h-3 { + height: 0.75rem; +} + +.h-14 { + height: 3.5rem; +} + +.h-4 { + height: 1rem; +} + +.h-5 { + height: 1.25rem; +} + +.h-6 { + height: 1.5rem; +} + +.h-9 { + height: 2.25rem; +} + +.h-2 { + height: 0.5rem; +} + +.h-10 { + height: 2.5rem; +} + +.h-12 { + height: 3rem; +} + +.h-7 { + height: 1.75rem; +} + +.h-11 { + height: 2.75rem; +} + +.h-64 { + height: 16rem; +} + +.max-h-screen { + max-height: 100vh; +} + +.max-h-\[40px\] { + max-height: 40px; +} + +.min-h-\[64px\] { + min-height: 64px; +} + +.w-screen { + width: 100vw; +} + +.w-32 { + width: 8rem; +} + +.w-8 { + width: 2rem; +} + +.w-full { + width: 100%; +} + +.w-1 { + width: 0.25rem; +} + +.w-96 { + width: 24rem; +} + +.w-20 { + width: 5rem; +} + +.w-\[344px\] { + width: 344px; +} + +.w-6 { + width: 1.5rem; +} + +.w-5 { + width: 1.25rem; +} + +.w-4 { + width: 1rem; +} + +.w-7 { + width: 1.75rem; +} + +.w-\[calc\(680px\/2\)\] { + width: calc(680px / 2); +} + +.w-72 { + width: 18rem; +} + +.w-\[300px\] { + width: 300px; +} + +.w-48 { + width: 12rem; +} + +.w-10 { + width: 2.5rem; +} + +.w-12 { + width: 3rem; +} + +.w-64 { + width: 16rem; +} + +.w-36 { + width: 9rem; +} + +.w-44 { + width: 11rem; +} + +.w-9 { + width: 2.25rem; +} + +.min-w-\[300px\] { + min-width: 300px; +} + +.min-w-full { + min-width: 100%; +} + +.min-w-0 { + min-width: 0px; +} + +.max-w-\[1100px\] { + max-width: 1100px; +} + +.max-w-\[700px\] { + max-width: 700px; +} + +.max-w-\[40px\] { + max-width: 40px; +} + +.max-w-prose { + max-width: 65ch; +} + +.flex-shrink-0 { + flex-shrink: 0; +} + +.shrink-0 { + flex-shrink: 0; +} + +.shrink { + flex-shrink: 1; +} + +.flex-grow { + flex-grow: 1; +} + +.grow { + flex-grow: 1; +} + +.grow-\[3\] { + flex-grow: 3; +} + +.grow-\[1\] { + flex-grow: 1; +} + +.grow-0 { + flex-grow: 0; +} + +.rotate-90 { + --tw-rotate: 90deg; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); +} + +.transform { + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); +} + +@keyframes spin { + to { + transform: rotate(360deg); + } +} + +.animate-spin { + animation: spin 1s linear infinite; +} + +.cursor-pointer { + cursor: pointer; +} + +.cursor-auto { + cursor: auto; +} + +.select-none { + -webkit-user-select: none; + -moz-user-select: none; + user-select: none; +} + +.resize { + resize: both; +} + +.appearance-none { + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; +} + +.grid-cols-4 { + grid-template-columns: repeat(4, minmax(0, 1fr)); +} + +.grid-cols-2 { + grid-template-columns: repeat(2, minmax(0, 1fr)); +} + +.grid-cols-3 { + grid-template-columns: repeat(3, minmax(0, 1fr)); +} + +.grid-rows-1 { + grid-template-rows: repeat(1, minmax(0, 1fr)); +} + +.flex-row { + flex-direction: row; +} + +.flex-col { + flex-direction: column; +} + +.flex-wrap { + flex-wrap: wrap; +} + +.items-center { + align-items: center; +} + +.justify-start { + justify-content: flex-start; +} + +.justify-end { + justify-content: flex-end; +} + +.justify-center { + justify-content: center; +} + +.justify-between { + justify-content: space-between; +} + +.gap-5 { + gap: 1.25rem; +} + +.gap-3 { + gap: 0.75rem; +} + +.gap-2 { + gap: 0.5rem; +} + +.gap-x-5 { + -moz-column-gap: 1.25rem; + column-gap: 1.25rem; +} + +.space-x-3 > :not([hidden]) ~ :not([hidden]) { + --tw-space-x-reverse: 0; + margin-right: calc(0.75rem * var(--tw-space-x-reverse)); + margin-left: calc(0.75rem * calc(1 - var(--tw-space-x-reverse))); +} + +.space-y-3 > :not([hidden]) ~ :not([hidden]) { + --tw-space-y-reverse: 0; + margin-top: calc(0.75rem * calc(1 - var(--tw-space-y-reverse))); + margin-bottom: calc(0.75rem * var(--tw-space-y-reverse)); +} + +.space-x-2 > :not([hidden]) ~ :not([hidden]) { + --tw-space-x-reverse: 0; + margin-right: calc(0.5rem * var(--tw-space-x-reverse)); + margin-left: calc(0.5rem * calc(1 - var(--tw-space-x-reverse))); +} + +.space-x-4 > :not([hidden]) ~ :not([hidden]) { + --tw-space-x-reverse: 0; + margin-right: calc(1rem * var(--tw-space-x-reverse)); + margin-left: calc(1rem * calc(1 - var(--tw-space-x-reverse))); +} + +.space-x-1 > :not([hidden]) ~ :not([hidden]) { + --tw-space-x-reverse: 0; + margin-right: calc(0.25rem * var(--tw-space-x-reverse)); + margin-left: calc(0.25rem * calc(1 - var(--tw-space-x-reverse))); +} + +.space-x-5 > :not([hidden]) ~ :not([hidden]) { + --tw-space-x-reverse: 0; + margin-right: calc(1.25rem * var(--tw-space-x-reverse)); + margin-left: calc(1.25rem * calc(1 - var(--tw-space-x-reverse))); +} + +.space-y-5 > :not([hidden]) ~ :not([hidden]) { + --tw-space-y-reverse: 0; + margin-top: calc(1.25rem * calc(1 - var(--tw-space-y-reverse))); + margin-bottom: calc(1.25rem * var(--tw-space-y-reverse)); +} + +.space-y-1 > :not([hidden]) ~ :not([hidden]) { + --tw-space-y-reverse: 0; + margin-top: calc(0.25rem * calc(1 - var(--tw-space-y-reverse))); + margin-bottom: calc(0.25rem * var(--tw-space-y-reverse)); +} + +.divide-y > :not([hidden]) ~ :not([hidden]) { + --tw-divide-y-reverse: 0; + border-top-width: calc(1px * calc(1 - var(--tw-divide-y-reverse))); + border-bottom-width: calc(1px * var(--tw-divide-y-reverse)); +} + +.divide-gray-200 > :not([hidden]) ~ :not([hidden]) { + --tw-divide-opacity: 1; + border-color: rgb(229 231 235 / var(--tw-divide-opacity)); +} + +.self-end { + align-self: flex-end; +} + +.self-center { + align-self: center; +} + +.overflow-auto { + overflow: auto; +} + +.overflow-hidden { + overflow: hidden; +} + +.overflow-scroll { + overflow: scroll; +} + +.truncate { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.whitespace-nowrap { + white-space: nowrap; +} + +.whitespace-pre-wrap { + white-space: pre-wrap; +} + +.break-all { + word-break: break-all; +} + +.rounded-lg { + border-radius: 0.5rem; +} + +.rounded { + border-radius: 0.25rem; +} + +.rounded-xl { + border-radius: 0.75rem; +} + +.rounded-full { + border-radius: 9999px; +} + +.rounded-md { + border-radius: 0.375rem; +} + +.rounded-l-md { + border-top-left-radius: 0.375rem; + border-bottom-left-radius: 0.375rem; +} + +.rounded-t-xl { + border-top-left-radius: 0.75rem; + border-top-right-radius: 0.75rem; +} + +.rounded-b-xl { + border-bottom-right-radius: 0.75rem; + border-bottom-left-radius: 0.75rem; +} + +.border { + border-width: 1px; +} + +.border-2 { + border-width: 2px; +} + +.border-b { + border-bottom-width: 1px; +} + +.border-b-2 { + border-bottom-width: 2px; +} + +.border-l-2 { + border-left-width: 2px; +} + +.border-r-2 { + border-right-width: 2px; +} + +.border-r { + border-right-width: 1px; +} + +.border-t { + border-top-width: 1px; +} + +.border-t-\[1px\] { + border-top-width: 1px; +} + +.border-solid { + border-style: solid; +} + +.border-dark-700 { + --tw-border-opacity: 1; + border-color: rgb(27 28 33 / var(--tw-border-opacity)); +} + +.border-dark-600 { + --tw-border-opacity: 1; + border-color: rgb(44 45 49 / var(--tw-border-opacity)); +} + +.border-transparent { + border-color: transparent; +} + +.border-link { + --tw-border-opacity: 1; + border-color: rgb(41 151 255 / var(--tw-border-opacity)); +} + +.bg-dark-900 { + --tw-bg-opacity: 1; + background-color: rgb(15 16 21 / var(--tw-bg-opacity)); +} + +.bg-dark-700 { + --tw-bg-opacity: 1; + background-color: rgb(27 28 33 / var(--tw-bg-opacity)); +} + +.bg-accent { + --tw-bg-opacity: 1; + background-color: rgb(46 92 255 / var(--tw-bg-opacity)); +} + +.bg-dark-800 { + --tw-bg-opacity: 1; + background-color: rgb(20 21 26 / var(--tw-bg-opacity)); +} + +.bg-dark-600 { + --tw-bg-opacity: 1; + background-color: rgb(44 45 49 / var(--tw-bg-opacity)); +} + +.bg-white\/10 { + background-color: rgb(255 255 255 / 0.1); +} + +.bg-dark-500 { + --tw-bg-opacity: 1; + background-color: rgb(63 64 68 / var(--tw-bg-opacity)); +} + +.bg-red-100 { + --tw-bg-opacity: 1; + background-color: rgb(254 226 226 / var(--tw-bg-opacity)); +} + +.bg-orange-100 { + --tw-bg-opacity: 1; + background-color: rgb(255 237 213 / var(--tw-bg-opacity)); +} + +.bg-white { + --tw-bg-opacity: 1; + background-color: rgb(255 255 255 / var(--tw-bg-opacity)); +} + +.bg-black\/40 { + background-color: rgb(0 0 0 / 0.4); +} + +.bg-dark-400 { + --tw-bg-opacity: 1; + background-color: rgb(87 88 91 / var(--tw-bg-opacity)); +} + +.bg-red-800 { + --tw-bg-opacity: 1; + background-color: rgb(153 27 27 / var(--tw-bg-opacity)); +} + +.bg-gray-50 { + --tw-bg-opacity: 1; + background-color: rgb(249 250 251 / var(--tw-bg-opacity)); +} + +.bg-dark-800\/70 { + background-color: rgb(20 21 26 / 0.7); +} + +.bg-red-600 { + --tw-bg-opacity: 1; + background-color: rgb(220 38 38 / var(--tw-bg-opacity)); +} + +.fill-current { + fill: currentColor; +} + +.p-4 { + padding: 1rem; +} + +.p-3 { + padding: 0.75rem; +} + +.p-1 { + padding: 0.25rem; +} + +.p-5 { + padding: 1.25rem; +} + +.p-2 { + padding: 0.5rem; +} + +.px-\[calc\(\(100\%-700px\)\/2\)\] { + padding-left: calc((100% - 700px) / 2); + padding-right: calc((100% - 700px) / 2); +} + +.py-20 { + padding-top: 5rem; + padding-bottom: 5rem; +} + +.px-2 { + padding-left: 0.5rem; + padding-right: 0.5rem; +} + +.py-2 { + padding-top: 0.5rem; + padding-bottom: 0.5rem; +} + +.py-1 { + padding-top: 0.25rem; + padding-bottom: 0.25rem; +} + +.px-3 { + padding-left: 0.75rem; + padding-right: 0.75rem; +} + +.px-1\.5 { + padding-left: 0.375rem; + padding-right: 0.375rem; +} + +.px-1 { + padding-left: 0.25rem; + padding-right: 0.25rem; +} + +.px-6 { + padding-left: 1.5rem; + padding-right: 1.5rem; +} + +.py-3 { + padding-top: 0.75rem; + padding-bottom: 0.75rem; +} + +.px-4 { + padding-left: 1rem; + padding-right: 1rem; +} + +.py-5 { + padding-top: 1.25rem; + padding-bottom: 1.25rem; +} + +.py-4 { + padding-top: 1rem; + padding-bottom: 1rem; +} + +.pr-1 { + padding-right: 0.25rem; +} + +.pb-6 { + padding-bottom: 1.5rem; +} + +.pr-3 { + padding-right: 0.75rem; +} + +.pt-3 { + padding-top: 0.75rem; +} + +.pl-1 { + padding-left: 0.25rem; +} + +.pb-4 { + padding-bottom: 1rem; +} + +.pb-0 { + padding-bottom: 0px; +} + +.pr-8 { + padding-right: 2rem; +} + +.pt-8 { + padding-top: 2rem; +} + +.pl-2 { + padding-left: 0.5rem; +} + +.pl-4 { + padding-left: 1rem; +} + +.text-left { + text-align: left; +} + +.text-center { + text-align: center; +} + +.text-right { + text-align: right; +} + +.align-middle { + vertical-align: middle; +} + +.text-lg { + font-size: 1.125rem; + line-height: 1.75rem; +} + +.text-\[0px\] { + font-size: 0px; +} + +.text-base { + font-size: 1rem; + line-height: 1.5rem; +} + +.text-sm { + font-size: 0.875rem; + line-height: 1.25rem; +} + +.text-xs { + font-size: 0.75rem; + line-height: 1rem; +} + +.font-medium { + font-weight: 500; +} + +.uppercase { + text-transform: uppercase; +} + +.capitalize { + text-transform: capitalize; +} + +.italic { + font-style: italic; +} + +.leading-none { + line-height: 1; +} + +.leading-4 { + line-height: 1rem; +} + +.tracking-wider { + letter-spacing: 0.05em; +} + +.text-white { + --tw-text-opacity: 1; + color: rgb(255 255 255 / var(--tw-text-opacity)); +} + +.text-dark-200 { + --tw-text-opacity: 1; + color: rgb(159 159 161 / var(--tw-text-opacity)); +} + +.text-red-700 { + --tw-text-opacity: 1; + color: rgb(185 28 28 / var(--tw-text-opacity)); +} + +.text-orange-700 { + --tw-text-opacity: 1; + color: rgb(194 65 12 / var(--tw-text-opacity)); +} + +.text-black { + --tw-text-opacity: 1; + color: rgb(0 0 0 / var(--tw-text-opacity)); +} + +.text-dark-100 { + --tw-text-opacity: 1; + color: rgb(207 207 208 / var(--tw-text-opacity)); +} + +.text-accent { + --tw-text-opacity: 1; + color: rgb(46 92 255 / var(--tw-text-opacity)); +} + +.text-link { + --tw-text-opacity: 1; + color: rgb(41 151 255 / var(--tw-text-opacity)); +} + +.text-dark-300 { + --tw-text-opacity: 1; + color: rgb(111 112 115 / var(--tw-text-opacity)); +} + +.text-gray-500 { + --tw-text-opacity: 1; + color: rgb(107 114 128 / var(--tw-text-opacity)); +} + +.underline { + text-decoration-line: underline; +} + +.no-underline { + text-decoration-line: none; +} + +.opacity-20 { + opacity: 0.2; +} + +.opacity-0 { + opacity: 0; +} + +.opacity-40 { + opacity: 0.4; +} + +.opacity-100 { + opacity: 1; +} + +.shadow { + --tw-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1); + --tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color), 0 1px 2px -1px var(--tw-shadow-color); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); +} + +.shadow-md { + --tw-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1); + --tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); +} + +.outline-none { + outline: 2px solid transparent; + outline-offset: 2px; +} + +.outline { + outline-style: solid; +} + +.grayscale { + --tw-grayscale: grayscale(100%); + filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow); +} + +.invert { + --tw-invert: invert(100%); + filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow); +} + +.sepia { + --tw-sepia: sepia(100%); + filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow); +} + +.filter { + filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow); +} + +.backdrop-blur { + --tw-backdrop-blur: blur(8px); + -webkit-backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia); + backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia); +} + +.transition { + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, -webkit-backdrop-filter; + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter; + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter, -webkit-backdrop-filter; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; +} + +.transition-all { + transition-property: all; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; +} + +.duration-100 { + transition-duration: 100ms; +} + +.duration-200 { + transition-duration: 200ms; +} + +.ease-out { + transition-timing-function: cubic-bezier(0, 0, 0.2, 1); +} + +* { + font-family: "Social"; + font-style: normal; + font-variant: normal; + text-transform: none; + -webkit-font-smoothing: antialiased; +} + +/* + * + * ScrollBars + * + */ + +*::-webkit-scrollbar, +*::-webkit-scrollbar-track-piece, +*::-webkit-scrollbar-corner { + width: 0.5rem !important; + background-color: transparent !important; +} + +*::-webkit-scrollbar-thumb { + border-radius: 0.75rem; + --tw-bg-opacity: 1 !important; + background-color: rgb(44 45 49 / var(--tw-bg-opacity)) !important; +} + +.hover\:border-dark-600:hover { + --tw-border-opacity: 1; + border-color: rgb(44 45 49 / var(--tw-border-opacity)); +} + +.hover\:border-dark-800:hover { + --tw-border-opacity: 1; + border-color: rgb(20 21 26 / var(--tw-border-opacity)); +} + +.hover\:bg-dark-700:hover { + --tw-bg-opacity: 1; + background-color: rgb(27 28 33 / var(--tw-bg-opacity)); +} + +.hover\:bg-dark-800:hover { + --tw-bg-opacity: 1; + background-color: rgb(20 21 26 / var(--tw-bg-opacity)); +} + +.hover\:bg-dark-500:hover { + --tw-bg-opacity: 1; + background-color: rgb(63 64 68 / var(--tw-bg-opacity)); +} + +.hover\:text-white:hover { + --tw-text-opacity: 1; + color: rgb(255 255 255 / var(--tw-text-opacity)); +} + +.hover\:text-dark-50:hover { + --tw-text-opacity: 1; + color: rgb(231 231 232 / var(--tw-text-opacity)); +} + +.hover\:underline:hover { + text-decoration-line: underline; +} + +.hover\:opacity-80:hover { + opacity: 0.8; +} + +.group:hover .group-hover\:ml-3 { + margin-left: 0.75rem; +} + +.group:hover .group-hover\:flex { + display: flex; +} + +.group:hover .group-hover\:hidden { + display: none; +} + +.group:hover .group-hover\:opacity-100 { + opacity: 1; +} + +.peer:checked ~ .peer-checked\:block { + display: block; +} + +.peer:checked ~ .peer-checked\:flex { + display: flex; +} + +.peer:checked ~ .peer-checked\:hidden { + display: none; +} + +.peer:checked ~ .peer-checked\:border-accent { + --tw-border-opacity: 1; + border-color: rgb(46 92 255 / var(--tw-border-opacity)); +} + +.peer:checked ~ .peer-checked\:bg-dark-700 { + --tw-bg-opacity: 1; + background-color: rgb(27 28 33 / var(--tw-bg-opacity)); +} + +.aria-checked\:border-b-2[aria-checked="true"] { + border-bottom-width: 2px; +} + +.aria-checked\:border-link[aria-checked="true"] { + --tw-border-opacity: 1; + border-color: rgb(41 151 255 / var(--tw-border-opacity)); +} + +.aria-checked\:text-link[aria-checked="true"] { + --tw-text-opacity: 1; + color: rgb(41 151 255 / var(--tw-text-opacity)); +} + +@media (prefers-color-scheme: dark) { + .dark\:bg-gray-700 { + --tw-bg-opacity: 1; + background-color: rgb(55 65 81 / var(--tw-bg-opacity)); + } +} + +@media (min-width: 1024px) { + .lg\:flex { + display: flex; + } +} diff --git a/pyinstaller/electron/preload.js b/pyinstaller/electron/src/preload.js similarity index 60% rename from pyinstaller/electron/preload.js rename to pyinstaller/electron/src/preload.js index c2ff9c1697..9811c142c1 100644 --- a/pyinstaller/electron/preload.js +++ b/pyinstaller/electron/src/preload.js @@ -1,11 +1,19 @@ // All of the Node.js APIs are available in the preload process. // It has the same sandbox as a Chrome extension. + +const { contextBridge, ipcRenderer } = require('electron') + +contextBridge.exposeInMainWorld('api', { + send: (channel, data) => ipcRenderer.send(channel, data), + receive: (channel, func) => ipcRenderer.on(channel, (event, ...args) => func(...args)), +}) + window.addEventListener('DOMContentLoaded', () => { + // No idea why this is here const replaceText = (selector, text) => { const element = document.getElementById(selector) if (element) element.innerText = text } - for (const type of ['chrome', 'node', 'electron']) { replaceText(`${type}-version`, process.versions[type]) } diff --git a/pyinstaller/electron/src/renderer.js b/pyinstaller/electron/src/renderer.js new file mode 100644 index 0000000000..47d051290b --- /dev/null +++ b/pyinstaller/electron/src/renderer.js @@ -0,0 +1,28 @@ +// This file is required by the index.html file and will +// be executed in the renderer process for that window. +// No Node.js APIs are available in this process because +// `nodeIntegration` is turned off. Use `preload.js` to +// selectively enable features needed in the rendering +// process. + +// All of the Node.js APIs are available in the preload process. +// It has the same sandbox as a Chrome extension. +window.addEventListener('DOMContentLoaded', () => { + + + const updateSpinner = (show) => { + const spinnerElement = document.getElementById('spinner'); + if (spinnerElement) { + spinnerElement.classList.toggle('hidden', !show); + } + }; + window.api.receive('update-loader-message', (data) => { + const launchTextElement = document.getElementById('launch-text'); + if (launchTextElement) { + launchTextElement.textContent = data.msg; + updateSpinner(data.showSpinner); + } + }); + + + }) \ No newline at end of file diff --git a/pyinstaller/electron/src/specterd.js b/pyinstaller/electron/src/specterd.js new file mode 100644 index 0000000000..7c2a4c3169 --- /dev/null +++ b/pyinstaller/electron/src/specterd.js @@ -0,0 +1,189 @@ +const { app, BrowserWindow } = require('electron') +const { appSettings, platformName } = require('./config') +const { + showError, + updateSpecterdStatus, + updatingLoaderMsg, + createWindow, + loadUrl, + executeJavaScript, +} = require('./uiHelpers') +const { logger } = require('./logging') +const { spawn } = require('child_process') +const { URL } = require('url') + +function checkSpecterd(logs, specterdStarted) { + // There doesn't seem to be another more straightforward way to check whether specterd is running: https://github.com/nodejs/help/issues/1191 + // Setting a timeout to avoid waiting for specterd endlessly + const timeout = 180000 // 3 minutes + const now = Date.now() + const timeElapsed = now - specterdStarted + if (timeElapsed > timeout) { + return 'timeout' + } + if (logs.toString().includes('Serving Flask app')) { + return 'running' + } else { + return 'not running' + } +} + +let specterIsRunning = false +function startSpecterd(specterdPath, automaticWalletImport = false) { + if (platformName == 'win64') { + specterdPath += '.exe' + } + let hwiBridgeMode = appSettings.mode == 'hwibridge' + updatingLoaderMsg('Launching Specter ...', (showSpinner = 'true')) + updateSpecterdStatus('Launching Specter ...') + let specterdArgs = ['server'] + specterdArgs.push('--no-filelog') + if (hwiBridgeMode) specterdArgs.push('--hwibridge') + if (appSettings.specterdCLIArgs != '') { + // User has inputed cli arguments in the UI + let specterdExtraArgs = appSettings.specterdCLIArgs.split(' ') + specterdExtraArgs.forEach((arg) => { + // Ensures that whitespaces are not used as cli arguments + if (arg != '') { + specterdArgs.push(arg) + } + }) + } + // locale fix (copying from nodejs-env + adding locales) + const options = { + env: { ...process.env }, + } + options.env['LC_ALL'] = 'en_US.utf-8' + options.env['LANG'] = 'en_US.utf-8' + options.env['SPECTER_LOGFORMAT'] = 'SPECTERD: %(levelname)s in %(module)s: %(message)s' + logger.info(`Starting specter: ${specterdPath} ${specterdArgs.join(' ')}`) + specterdProcess = spawn(specterdPath, specterdArgs, options) + const specterdStarted = Date.now() + + // We are checking for both, stdout and stderr, to be on the save side. + specterdProcess.stdout.on('data', (data) => { + actOnNewLogLine(data.toString(), 'stdout') + }) + + specterdProcess.stderr.on('data', (data) => { + actOnNewLogLine(data.toString(), 'stderr') + }) + + const actOnNewLogLine = (logLine, origin) => { + logger.info(`${origin}: ${logLine.replace(/(\r\n|\n|\r)/gm, '')}`) + const serverdStatus = checkSpecterd(logLine, specterdStarted) + if (!specterIsRunning) { + if (serverdStatus === 'running') { + logger.info(`Specter server seems to run ...`) + updateSpecterdStatus('Specter is running') + specterIsRunning = true + if (automaticWalletImport === true) { + logger.info('Performing automatic wallet import ...') + updatingLoaderMsg('Launching wallet importer. This will only work with a node connection.', (showSpinner = true)) + setTimeout(() => { + importWallet(walletDataFromUrl) + }, 3000) + } else { + logger.info('Normal startup of Specter.') + createWindow(appSettings.specterURL) + } + } else if (serverdStatus === 'timeout') { + showError('Specter does not seem to start. Check the logs in the menu for more details.') + updateSpecterdStatus('Specter does not start') + logger.error('Startup timeout for specterd exceeded') + } else { + updatingLoaderMsg('Still waiting for Specter to start ...') + updateSpecterdStatus('Specter is starting') + } + } + } + + specterdProcess.on('exit', (code) => { + if (code !== 0) { + logger.error(`specterd exited with code ${code}`) + showError(`Specter exited with exit code ${code}. Check the logs in the menu for more details.`) + } + }) + + specterdProcess.on('error', (err) => { + logger.error(`Error starting Specter server: ${err}`) + showError(`Specter failed to start, due to ${err.message}. Check the logs in the menu for more details.`) + }) + + app.on('activate', function () { + // On macOS it's common to re-create a window in the app when the + // dock icon is clicked and there are no other windows open. + if (BrowserWindow.getAllWindows().length === 0) createWindow(appSettings.specterURL) + }) + // since these are streams, you can pipe them elsewhere + specterdProcess.on('close', (code) => { + updateSpecterdStatus('Specter stopped...') + logger.info(`child process exited with code ${code}`) + }) +} + +function quitSpecterd() { + if (specterdProcess) { + try { + if (platformName == 'win64') { + exec('taskkill /F /T /PID ' + specterdProcess.pid) + exec('taskkill /IM specterd.exe ') + process.kill(-specterdProcess.pid) + } + specterdProcess.kill('SIGINT') + } catch (e) { + logger.info('Specterd quit warning: ' + e) + } + } +} + +let walletDataFromUrl +// Checking whether the app was opened via a Specter URL and determine whether to perform a specific startup action +app.on('open-url', (_, url) => { + logger.info('The app was opened via URL, checking the URL to decide whether to do any automatic actions ...') + // Parse the URL to extract the query parameters + const specterUrl = new URL(url) + const searchParams = specterUrl.searchParams + // Get the query parameter values + const action = searchParams.get('action') + const data = searchParams.get('data') + if (action === 'importWallet' && data !== '') { + logger.info('Automatic wallet import identified in the URL, setting automaticWalletImport to true.') + automaticWalletImport = true + walletDataFromUrl = data + // Directly import if the app and specterd is already running + if (specterIsRunning) { + logger.info('Performing automatic wallet import ...') + loadURL(`file://${__dirname}/splash.html`) + updatingLoaderMsg('Launching wallet importer. This will only work with a node connection.', (showSpinner = true)) + setTimeout(() => { + importWallet(walletDataFromUrl) + }, 3000) + } + } +}) + +// Automatically import the wallet json string, bring user to the final import wallet screen. +// Only proceed with the import if the importFromWalletSoftwareBtn can be found. +// If it is not, users are redirected by specterd to the configure connection screen. +function importWallet(walletData) { + loadUrl(appSettings.specterURL + '/wallets/new_wallet/') + let code = ` + const importFromWalletSoftwareBtn = document.getElementById('import-from-wallet-software-btn') + if (importFromWalletSoftwareBtn) { + importFromWalletSoftwareBtn.click() + const walletDataTextArea = document.getElementById('txt') + if (walletDataTextArea) { + walletDataTextArea.value = \`${walletData}\` + } + const continueBtn = document.getElementById('continue-with-wallet-import-btn'); + continueBtn.click() + } + ` + executeJavaScript(code) +} + +module.exports = { + startSpecterd, + quitSpecterd, +} diff --git a/pyinstaller/electron/splash.html b/pyinstaller/electron/src/splash.html similarity index 98% rename from pyinstaller/electron/splash.html rename to pyinstaller/electron/src/splash.html index 51d6fad407..f56b4579b5 100644 --- a/pyinstaller/electron/splash.html +++ b/pyinstaller/electron/src/splash.html @@ -6,4 +6,5 @@

Launching Specter Desktop...

+ diff --git a/pyinstaller/electron/src/typography.css b/pyinstaller/electron/src/typography.css new file mode 100644 index 0000000000..206994f0b6 --- /dev/null +++ b/pyinstaller/electron/src/typography.css @@ -0,0 +1,20 @@ +@font-face { + font-family: "Social"; + src: url("../fonts/Social-Regular.otf"); + font-weight: 400; + font-style: normal; +} + +@font-face { + font-family: "Social"; + src: url("../fonts/Social-Medium.otf"); + font-weight: 500; + font-style: normal; +} + +@font-face { + font-family: "Social"; + src: url("../fonts/Social-Bold.otf"); + font-weight: 700; + font-style: normal; +} diff --git a/pyinstaller/electron/src/uiHelpers.js b/pyinstaller/electron/src/uiHelpers.js new file mode 100644 index 0000000000..7ec6745027 --- /dev/null +++ b/pyinstaller/electron/src/uiHelpers.js @@ -0,0 +1,178 @@ +const path = require('path') +const { Menu, BrowserWindow, nativeTheme, app, Tray, nativeImage } = require('electron') +const { logger } = require('./logging') +const { appSettings, isDev, platformName } = require('./config') +const ProgressBar = require('electron-progressbar') +const { isMac } = require('./helpers') +const { shell } = require('electron') + +// Initialized with initMainWindow +let mainWindow +// Initialized with InitTray +let tray +let trayMenu + +const loadUrl = (url) => { + mainWindow.loadURL(url) +} + +const executeJavaScript = (code) => { + mainWindow.webContents.executeJavaScript(code) +} + +function showError(error) { + updatingLoaderMsg('Specter encountered an error:' + error.toString()) +} + +function updatingLoaderMsg(msg, showSpinner = false) { + if (mainWindow) { + // see preload.js where this is setup + mainWindow.webContents.send('update-loader-message', { + msg, + showSpinner, + }) + } else { + logger.error('mainWindow not initialized in updatingLoaderMsg') + } + logger.info('Updated LoaderMsg: ' + msg) +} + +function updateSpecterdStatus(status) { + trayMenu[0] = { label: status, enabled: false } + tray.setContextMenu(Menu.buildFromTemplate(trayMenu)) +} + +function createWindow(specterURL) { + if (!mainWindow) { + initMainWindow() + } + + // Create the browser window. + if (appSettings.tor) { + mainWindow.webContents.session.setProxy({ proxyRules: appSettings.proxyURL }) + } + mainWindow.loadURL(specterURL + '?mode=remote') +} + +let webPreferences = { + // worldSafeExecuteJavaScript: true, Removed in Electron 14 + contextIsolation: true, + preload: path.join(__dirname, 'preload.js'), +} + +function initMainWindow(dimensions) { + // In production we use the icons from the build folder + // Note: On MacOS setting an icon here as no effect + const iconPath = isDev ? path.join(__dirname, 'assets-dev/app_icon.png') : '' + mainWindow = new BrowserWindow({ + width: parseInt(dimensions.width * 0.8), + minWidth: 1120, + height: parseInt(dimensions.height * 0.8), + icon: iconPath, + webPreferences, + }) + + // Ensures that any links with target="_blank" or window.open() will be opened in the user's default browser instead of within the app + mainWindow.webContents.setWindowOpenHandler(({ url }) => { + shell.openExternal(url) + return { action: 'deny' } + }) + + mainWindow.webContents.on('did-fail-load', function () { + mainWindow.loadURL(`file://${__dirname}/splash.html`) + updatingLoaderMsg( + `Failed to load: ${appSettings.specterURL}
Please make sure the URL is entered correctly in the settings and try again...` + ) + }) + return mainWindow +} + +const initTray = (openPreferences, quitSpecterd) => { + if (isMac) { + const trayIconPath = nativeTheme.shouldUseDarkColors ? '/assets/menu_icon_dark.png' : '/assets/menu_icon_light.png' + const createTrayIcon = (trayIconPath) => { + let trayIcon = nativeImage.createFromPath(app.getAppPath() + trayIconPath) + // Resize + trayIcon = trayIcon.resize({ width: 22, height: 22 }) + return trayIcon + } + const trayIcon = createTrayIcon(trayIconPath) + tray = new Tray(trayIcon) + + // Change the tray icon if appearance is changed in Mac settings + const updateTrayIcon = () => { + logger.info('Updating tray icon ...') + const trayIconPath = nativeTheme.shouldUseDarkColors ? '/assets/menu_icon_dark.png' : '/assets/menu_icon_light.png' + const newTrayIcon = createTrayIcon(trayIconPath) + tray.setImage(newTrayIcon) + } + nativeTheme.on('updated', updateTrayIcon) + } else { + const trayIcon = nativeImage.createFromPath(app.getAppPath() + '/assets/menu_icon.png') + tray = new Tray(trayIcon) + } + + trayMenu = [ + { label: 'Launching Specter ...', enabled: false }, + { + label: 'Show Specter', + click() { + mainWindow.show() + }, + }, + { + label: 'Settings', + click() { + openPreferences() + }, + }, + { + label: 'Quit', + click() { + quitSpecterd() + app.quit() + }, + }, + ] + tray.setToolTip('Specter') + tray.setContextMenu(Menu.buildFromTemplate(trayMenu)) +} + +const createProgressBar = (totalBytes) => { + progressBar = new ProgressBar({ + indeterminate: false, + abortOnError: true, + text: 'Downloading the Specter binary from GitHub', + detail: + 'This can take several minutes depending on your Internet connection. Specter will start once the download is finished.', + maxValue: totalBytes, + browserWindow: { + parent: mainWindow, + }, + style: { + detail: { + 'margin-bottom': '12px', + }, + bar: { + 'background-color': '#fff', + }, + value: { + 'background-color': '#000', + }, + }, + }) + return progressBar +} + +module.exports = { + loadUrl, + executeJavaScript, + showError, + updatingLoaderMsg, + updateSpecterdStatus, + createWindow, + initMainWindow, + mainWindow, + initTray, + createProgressBar, +} diff --git a/pyinstaller/requirements.in b/pyinstaller/requirements.in index ffc6f7735b..4009242ccc 100644 --- a/pyinstaller/requirements.in +++ b/pyinstaller/requirements.in @@ -2,5 +2,3 @@ pyinstaller==5.2 pefile==2022.5.30 macholib pywin32-ctypes -babel==2.10.1 -pytz==2022.1 diff --git a/pyinstaller/requirements.txt b/pyinstaller/requirements.txt index 33bda39fe3..e9f7ea6398 100644 --- a/pyinstaller/requirements.txt +++ b/pyinstaller/requirements.txt @@ -1,6 +1,6 @@ # -# This file is autogenerated by pip-compile with python 3.10 -# To update, run: +# This file is autogenerated by pip-compile with Python 3.10 +# by the following command: # # pip-compile --generate-hashes requirements.in # @@ -10,17 +10,15 @@ altgraph==0.17 \ # via # macholib # pyinstaller -babel==2.10.1 \ - --hash=sha256:3f349e85ad3154559ac4930c3918247d319f21910d5ce4b25d439ed8693b98d2 \ - --hash=sha256:98aeaca086133efb3e1e2aad0396987490c8425929ddbcfe0550184fdc54cd13 - # via -r requirements.in future==0.18.2 \ --hash=sha256:b1bead90b70cf6ec3f0710ae53a525360fa360d306a86583adc6bf83a4db537d # via pefile macholib==1.14 \ --hash=sha256:0c436bc847e7b1d9bda0560351bf76d7caf930fb585a828d13608839ef42c432 \ --hash=sha256:c500f02867515e6c60a27875b408920d18332ddf96b4035ef03beddd782d4281 - # via -r requirements.in + # via + # -r requirements.in + # pyinstaller pefile==2022.5.30 \ --hash=sha256:a5488a3dd1fd021ce33f969780b88fe0f7eebb76eb20996d7318f307612a045b # via -r requirements.in @@ -41,12 +39,6 @@ pyinstaller-hooks-contrib==2022.8 \ --hash=sha256:c4210fc50282c9c6a918e485e0bfae9405592390508e3be9fde19acc2213da56 \ --hash=sha256:e46f099934dd4577fb1ddcf37a99fa04027c92f8f5291c8802f326345988d001 # via pyinstaller -pytz==2022.1 \ - --hash=sha256:1e760e2fe6a8163bc0b3d9a19c4f84342afa0a2affebfaa84b01b978a02ecaa7 \ - --hash=sha256:e68985985296d9a66a881eb3193b0906246245294a881e7c8afe623866ac6a5c - # via - # -r requirements.in - # babel pywin32-ctypes==0.2.0 \ --hash=sha256:24ffc3b341d457d48e8922352130cf2644024a4ff09762a2261fd34c36ee5942 \ --hash=sha256:9dc2d991b3479cc2df15930958b674a48a227d5361d413827a4cfd0b5876fc98 diff --git a/pyproject.toml b/pyproject.toml index fb795dc370..2cafcf2f5d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,8 +1,8 @@ [build-system] requires = [ "setuptools<=65.7.0", - "setuptools_scm[toml]>=6.2", - "babel==2.11.0", + "setuptools_scm[toml]>=6.4.2", + "babel==2.12.1", "build==0.10.0", "wheel", "twine" @@ -77,7 +77,7 @@ dependencies = {file = ["requirements.in"]} test = [ "black==22.3.0", "pre-commit==2.13.0", - "pip-tools==6.12.2", + "pip-tools==6.13", "pytest==7.1.2", "PySocks==1.7.1", "pytest-cov==2.10.1", @@ -85,4 +85,8 @@ test = [ "python-gitlab==2.10.1", # requirements for stuff in ./utils "requests==2.26.0", +] + +gendownloadpage = [ + "markdown==3.3.7" ] \ No newline at end of file diff --git a/requirements.in b/requirements.in index a034527ffd..eed76ed03b 100644 --- a/requirements.in +++ b/requirements.in @@ -1,13 +1,12 @@ -certifi==2022.12.7 -chardet==3.0.4 -Click==8.1.1 +certifi==2024.6.2 +Click==8.1.7 Flask==2.2.5 Flask-Babel==3.1.0 Flask-Cors==3.0.10 Flask-Login==0.6.3 Flask-RESTful==0.3.10 Flask-HTTPAuth==4.8.0 -hwi==2.1.1 +hwi==2.4.0 python-dotenv==0.21.1 requests==2.26.0 pysocks==1.7.1 @@ -15,25 +14,26 @@ six==1.16.0 stem==1.8.0 embit==0.6.1 psutil==5.9.0 -pyopenssl==23.0.0 -flask_wtf==0.15.1 +pyopenssl==24.1.0 +flask_wtf==1.2.1 pgpy==0.6.0 -cbor==1.0.0 -mnemonic==0.20 -cryptography==39.0.1 +cbor2==5.4.6 +mnemonic==0.21 +cryptography==42.0.7 Flask-APScheduler==1.12.4 gunicorn==20.1.0 simple-websocket==0.8.1 -protobuf==3.20.2 +protobuf==4.23.3 PyJWT==2.4.0 pytimeparse==1.1.8 psycopg2-binary==2.9.5 +aioitertools==0.11.0 # Extensions cryptoadvance-liquidissuer==0.2.4 specterext-exfund==0.1.7 specterext-faucet==0.1.2 -cryptoadvance.spectrum==0.6.4 +cryptoadvance.spectrum==0.6.7 specterext-stacktrack==0.3.0 # workarounds diff --git a/requirements.txt b/requirements.txt index b344edc6b3..fa3c84b179 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,116 +2,131 @@ # This file is autogenerated by pip-compile with Python 3.10 # by the following command: # -# pip-compile --generate-hashes --resolver=backtracking requirements.in +# pip-compile --generate-hashes requirements.in # +aioitertools==0.11.0 \ + --hash=sha256:04b95e3dab25b449def24d7df809411c10e62aab0cbe31a50ca4e68748c43394 \ + --hash=sha256:42c68b8dd3a69c2bf7f2233bf7df4bb58b557bca5252ac02ed5187bbc67d6831 + # via -r requirements.in aniso8601==9.0.1 \ --hash=sha256:1d2b7ef82963909e93c4f24ce48d4de9e66009a21bf1c1e1c85bdd0812fe412f \ --hash=sha256:72e3117667eedf66951bb2d93f4296a56b94b078a8a95905a052611fb3f1b973 # via flask-restful -apscheduler==3.10.0 \ - --hash=sha256:575299f20073c60a2cc9d4fa5906024cdde33c5c0ce6087c4e3c14be3b50fdd4 \ - --hash=sha256:a49fc23269218416f0e41890eea7a75ed6b284f10630dcfe866ab659621a3696 +apscheduler==3.10.4 \ + --hash=sha256:e6df071b27d9be898e486bc7940a7be50b4af2e9da7c08f0744a96d4bd4cef4a \ + --hash=sha256:fb91e8a768632a4756a585f79ec834e0e27aad5860bac7eaa523d9ccefd87661 # via flask-apscheduler -babel==2.12.1 \ - --hash=sha256:b4246fb7677d3b98f501a39d43396d3cafdc8eadb045f4a31be01863f655c610 \ - --hash=sha256:cc2d99999cd01d44420ae725a21c9e3711b3aadc7976d6147f622d8581963455 +babel==2.15.0 \ + --hash=sha256:08706bdad8d0a3413266ab61bd6c34d0c28d6e1e7badf40a2cebe67644e2e1fb \ + --hash=sha256:8daf0e265d05768bc6c7a314cf1321e9a123afc328cc635c18622a2f30a04413 # via flask-babel -base58==2.1.1 \ - --hash=sha256:11a36f4d3ce51dfc1043f3218591ac4eb1ceb172919cebe05b52a5bcc8d245c2 \ - --hash=sha256:c5d0cb3f5b6e81e8e35da5754388ddcc6d0d14b6c6a132cb93d69ed580a7278c - # via bitbox02 -bitbox02==6.1.1 \ - --hash=sha256:99503409d6c61899f8e11eb11e7a29866b2754cb02c03acf34cdef99755aedd8 \ - --hash=sha256:f37f1e571f06aa0a4005441ca1948b53e68e3bef0b642963303b42810e2b1486 - # via hwi -cbor==1.0.0 \ - --hash=sha256:13225a262ddf5615cbd9fd55a76a0d53069d18b07d2e9f19c39e6acb8609bbb6 +cbor2==5.4.6 \ + --hash=sha256:0b956f19e93ba3180c336282cd1b6665631f2d3a196a9c19b29a833bf979e7a4 \ + --hash=sha256:0bd12c54a48949d11f5ffc2fa27f5df1b4754111f5207453e5fae3512ebb3cab \ + --hash=sha256:0d2b926b024d3a1549b819bc82fdc387062bbd977b0299dd5fa5e0ea3267b98b \ + --hash=sha256:1618d16e310f7ffed141762b0ff5d8bb6b53ad449406115cc465bf04213cefcf \ + --hash=sha256:181ac494091d1f9c5bb373cd85514ce1eb967a8cf3ec298e8dfa8878aa823956 \ + --hash=sha256:1835536e76ea16e88c934aac5e369ba9f93d495b01e5fa2d93f0b4986b89146d \ + --hash=sha256:1c12c0ab78f5bc290b08a79152a8621822415836a86f8f4b50dadba371736fda \ + --hash=sha256:24144822f8d2b0156f4cda9427f071f969c18683ffed39663dc86bc0a75ae4dd \ + --hash=sha256:309fffbb7f561d67f02095d4b9657b73c9220558701c997e9bfcfbca2696e927 \ + --hash=sha256:3316f09a77af85e7772ecfdd693b0f450678a60b1aee641bac319289757e3fa0 \ + --hash=sha256:3545b16f9f0d5f34d4c99052829c3726020a07be34c99c250d0df87418f02954 \ + --hash=sha256:39452c799453f5bf33281ffc0752c620b8bfa0b7c13070b87d370257a1311976 \ + --hash=sha256:3950be57a1698086cf26d8710b4e5a637b65133c5b1f9eec23967d4089d8cfed \ + --hash=sha256:456cdff668a50a52fdb8aa6d0742511e43ed46d6a5b463dba80a5a720fa0d320 \ + --hash=sha256:4b9f3924da0e460a93b3674c7e71020dd6c9e9f17400a34e52a88c0af2dcd2aa \ + --hash=sha256:4bbbdb2e3ef274865dc3f279aae109b5d94f4654aea3c72c479fb37e4a1e7ed7 \ + --hash=sha256:4ce1a2c272ba8523a55ea2f1d66e3464e89fa0e37c9a3d786a919fe64e68dbd7 \ + --hash=sha256:56dfa030cd3d67e5b6701d3067923f2f61536a8ffb1b45be14775d1e866b59ae \ + --hash=sha256:6709d97695205cd08255363b54afa035306d5302b7b5e38308c8ff5a47e60f2a \ + --hash=sha256:6e1b5aee920b6a2f737aa12e2b54de3826b09f885a7ce402db84216343368140 \ + --hash=sha256:6f9c702bee2954fffdfa3de95a5af1a6b1c5f155e39490353d5654d83bb05bb9 \ + --hash=sha256:78304df140b9e13b93bcbb2aecee64c9aaa9f1cadbd45f043b5e7b93cc2f21a2 \ + --hash=sha256:79e048e623846d60d735bb350263e8fdd36cb6195d7f1a2b57eacd573d9c0b33 \ + --hash=sha256:7bbd3470eb685325398023e335be896b74f61b014896604ed45049a7b7b6d8ac \ + --hash=sha256:80ac8ba450c7a41c5afe5f7e503d3092442ed75393e1de162b0bf0d97edf7c7f \ + --hash=sha256:9394ca49ecdf0957924e45d09a4026482d184a465a047f60c4044eb464c43de9 \ + --hash=sha256:94f844d0e232aca061a86dd6ff191e47ba0389ddd34acb784ad9a41594dc99a4 \ + --hash=sha256:96087fa5336ebfc94465c0768cd5de0fcf9af3840d2cf0ce32f5767855f1a293 \ + --hash=sha256:b893500db0fe033e570c3adc956af6eefc57e280026bd2d86fd53da9f1e594d7 \ + --hash=sha256:c285a2cb2c04004bfead93df89d92a0cef1874ad337d0cb5ea53c2c31e97bfdb \ + --hash=sha256:d2984a488f350aee1d54fa9cb8c6a3c1f1f5b268abbc91161e47185de4d829f3 \ + --hash=sha256:d54bd840b4fe34f097b8665fc0692c7dd175349e53976be6c5de4433b970daa4 \ + --hash=sha256:db9eb582fce972f0fa429d8159b7891ff8deccb7affc4995090afc61ce0d328a \ + --hash=sha256:e5094562dfe3e5583202b93ef7ca5082c2ba5571accb2c4412d27b7d0ba8a563 \ + --hash=sha256:e73ca40dd3c7210ff776acff9869ddc9ff67bae7c425b58e5715dcf55275163f \ + --hash=sha256:ff95b33e5482313a74648ca3620c9328e9f30ecfa034df040b828e476597d352 # via # -r requirements.in # hwi -certifi==2022.12.7 \ - --hash=sha256:35824b4c3a97115964b408844d64aa14db1cc518f6562e8d7261699d1350a9e3 \ - --hash=sha256:4ad3232f5e926d6718ec31cfc1fcadfde020920e278684144551c91769c7bc18 +certifi==2024.6.2 \ + --hash=sha256:3cd43f1c6fa7dedc5899d69d3ad0398fd018ad1a17fba83ddaf78aa46c747516 \ + --hash=sha256:ddc6c8ce995e6987e7faf5e3f1b02b302836a0e5d98ece18392cb1a36c72ad56 # via # -r requirements.in # requests -cffi==1.15.1 \ - --hash=sha256:00a9ed42e88df81ffae7a8ab6d9356b371399b91dbdf0c3cb1e84c03a13aceb5 \ - --hash=sha256:03425bdae262c76aad70202debd780501fabeaca237cdfddc008987c0e0f59ef \ - --hash=sha256:04ed324bda3cda42b9b695d51bb7d54b680b9719cfab04227cdd1e04e5de3104 \ - --hash=sha256:0e2642fe3142e4cc4af0799748233ad6da94c62a8bec3a6648bf8ee68b1c7426 \ - --hash=sha256:173379135477dc8cac4bc58f45db08ab45d228b3363adb7af79436135d028405 \ - --hash=sha256:198caafb44239b60e252492445da556afafc7d1e3ab7a1fb3f0584ef6d742375 \ - --hash=sha256:1e74c6b51a9ed6589199c787bf5f9875612ca4a8a0785fb2d4a84429badaf22a \ - --hash=sha256:2012c72d854c2d03e45d06ae57f40d78e5770d252f195b93f581acf3ba44496e \ - --hash=sha256:21157295583fe8943475029ed5abdcf71eb3911894724e360acff1d61c1d54bc \ - --hash=sha256:2470043b93ff09bf8fb1d46d1cb756ce6132c54826661a32d4e4d132e1977adf \ - --hash=sha256:285d29981935eb726a4399badae8f0ffdff4f5050eaa6d0cfc3f64b857b77185 \ - --hash=sha256:30d78fbc8ebf9c92c9b7823ee18eb92f2e6ef79b45ac84db507f52fbe3ec4497 \ - --hash=sha256:320dab6e7cb2eacdf0e658569d2575c4dad258c0fcc794f46215e1e39f90f2c3 \ - --hash=sha256:33ab79603146aace82c2427da5ca6e58f2b3f2fb5da893ceac0c42218a40be35 \ - --hash=sha256:3548db281cd7d2561c9ad9984681c95f7b0e38881201e157833a2342c30d5e8c \ - --hash=sha256:3799aecf2e17cf585d977b780ce79ff0dc9b78d799fc694221ce814c2c19db83 \ - --hash=sha256:39d39875251ca8f612b6f33e6b1195af86d1b3e60086068be9cc053aa4376e21 \ - --hash=sha256:3b926aa83d1edb5aa5b427b4053dc420ec295a08e40911296b9eb1b6170f6cca \ - --hash=sha256:3bcde07039e586f91b45c88f8583ea7cf7a0770df3a1649627bf598332cb6984 \ - --hash=sha256:3d08afd128ddaa624a48cf2b859afef385b720bb4b43df214f85616922e6a5ac \ - --hash=sha256:3eb6971dcff08619f8d91607cfc726518b6fa2a9eba42856be181c6d0d9515fd \ - --hash=sha256:40f4774f5a9d4f5e344f31a32b5096977b5d48560c5592e2f3d2c4374bd543ee \ - --hash=sha256:4289fc34b2f5316fbb762d75362931e351941fa95fa18789191b33fc4cf9504a \ - --hash=sha256:470c103ae716238bbe698d67ad020e1db9d9dba34fa5a899b5e21577e6d52ed2 \ - --hash=sha256:4f2c9f67e9821cad2e5f480bc8d83b8742896f1242dba247911072d4fa94c192 \ - --hash=sha256:50a74364d85fd319352182ef59c5c790484a336f6db772c1a9231f1c3ed0cbd7 \ - --hash=sha256:54a2db7b78338edd780e7ef7f9f6c442500fb0d41a5a4ea24fff1c929d5af585 \ - --hash=sha256:5635bd9cb9731e6d4a1132a498dd34f764034a8ce60cef4f5319c0541159392f \ - --hash=sha256:59c0b02d0a6c384d453fece7566d1c7e6b7bae4fc5874ef2ef46d56776d61c9e \ - --hash=sha256:5d598b938678ebf3c67377cdd45e09d431369c3b1a5b331058c338e201f12b27 \ - --hash=sha256:5df2768244d19ab7f60546d0c7c63ce1581f7af8b5de3eb3004b9b6fc8a9f84b \ - --hash=sha256:5ef34d190326c3b1f822a5b7a45f6c4535e2f47ed06fec77d3d799c450b2651e \ - --hash=sha256:6975a3fac6bc83c4a65c9f9fcab9e47019a11d3d2cf7f3c0d03431bf145a941e \ - --hash=sha256:6c9a799e985904922a4d207a94eae35c78ebae90e128f0c4e521ce339396be9d \ - --hash=sha256:70df4e3b545a17496c9b3f41f5115e69a4f2e77e94e1d2a8e1070bc0c38c8a3c \ - --hash=sha256:7473e861101c9e72452f9bf8acb984947aa1661a7704553a9f6e4baa5ba64415 \ - --hash=sha256:8102eaf27e1e448db915d08afa8b41d6c7ca7a04b7d73af6514df10a3e74bd82 \ - --hash=sha256:87c450779d0914f2861b8526e035c5e6da0a3199d8f1add1a665e1cbc6fc6d02 \ - --hash=sha256:8b7ee99e510d7b66cdb6c593f21c043c248537a32e0bedf02e01e9553a172314 \ - --hash=sha256:91fc98adde3d7881af9b59ed0294046f3806221863722ba7d8d120c575314325 \ - --hash=sha256:94411f22c3985acaec6f83c6df553f2dbe17b698cc7f8ae751ff2237d96b9e3c \ - --hash=sha256:98d85c6a2bef81588d9227dde12db8a7f47f639f4a17c9ae08e773aa9c697bf3 \ - --hash=sha256:9ad5db27f9cabae298d151c85cf2bad1d359a1b9c686a275df03385758e2f914 \ - --hash=sha256:a0b71b1b8fbf2b96e41c4d990244165e2c9be83d54962a9a1d118fd8657d2045 \ - --hash=sha256:a0f100c8912c114ff53e1202d0078b425bee3649ae34d7b070e9697f93c5d52d \ - --hash=sha256:a591fe9e525846e4d154205572a029f653ada1a78b93697f3b5a8f1f2bc055b9 \ - --hash=sha256:a5c84c68147988265e60416b57fc83425a78058853509c1b0629c180094904a5 \ - --hash=sha256:a66d3508133af6e8548451b25058d5812812ec3798c886bf38ed24a98216fab2 \ - --hash=sha256:a8c4917bd7ad33e8eb21e9a5bbba979b49d9a97acb3a803092cbc1133e20343c \ - --hash=sha256:b3bbeb01c2b273cca1e1e0c5df57f12dce9a4dd331b4fa1635b8bec26350bde3 \ - --hash=sha256:cba9d6b9a7d64d4bd46167096fc9d2f835e25d7e4c121fb2ddfc6528fb0413b2 \ - --hash=sha256:cc4d65aeeaa04136a12677d3dd0b1c0c94dc43abac5860ab33cceb42b801c1e8 \ - --hash=sha256:ce4bcc037df4fc5e3d184794f27bdaab018943698f4ca31630bc7f84a7b69c6d \ - --hash=sha256:cec7d9412a9102bdc577382c3929b337320c4c4c4849f2c5cdd14d7368c5562d \ - --hash=sha256:d400bfb9a37b1351253cb402671cea7e89bdecc294e8016a707f6d1d8ac934f9 \ - --hash=sha256:d61f4695e6c866a23a21acab0509af1cdfd2c013cf256bbf5b6b5e2695827162 \ - --hash=sha256:db0fbb9c62743ce59a9ff687eb5f4afbe77e5e8403d6697f7446e5f609976f76 \ - --hash=sha256:dd86c085fae2efd48ac91dd7ccffcfc0571387fe1193d33b6394db7ef31fe2a4 \ - --hash=sha256:e00b098126fd45523dd056d2efba6c5a63b71ffe9f2bbe1a4fe1716e1d0c331e \ - --hash=sha256:e229a521186c75c8ad9490854fd8bbdd9a0c9aa3a524326b55be83b54d4e0ad9 \ - --hash=sha256:e263d77ee3dd201c3a142934a086a4450861778baaeeb45db4591ef65550b0a6 \ - --hash=sha256:ed9cb427ba5504c1dc15ede7d516b84757c3e3d7868ccc85121d9310d27eed0b \ - --hash=sha256:fa6693661a4c91757f4412306191b6dc88c1703f780c8234035eac011922bc01 \ - --hash=sha256:fcd131dd944808b5bdb38e6f5b53013c5aa4f334c5cad0c72742f6eba4b73db0 +cffi==1.16.0 \ + --hash=sha256:0c9ef6ff37e974b73c25eecc13952c55bceed9112be2d9d938ded8e856138bcc \ + --hash=sha256:131fd094d1065b19540c3d72594260f118b231090295d8c34e19a7bbcf2e860a \ + --hash=sha256:1b8ebc27c014c59692bb2664c7d13ce7a6e9a629be20e54e7271fa696ff2b417 \ + --hash=sha256:2c56b361916f390cd758a57f2e16233eb4f64bcbeee88a4881ea90fca14dc6ab \ + --hash=sha256:2d92b25dbf6cae33f65005baf472d2c245c050b1ce709cc4588cdcdd5495b520 \ + --hash=sha256:31d13b0f99e0836b7ff893d37af07366ebc90b678b6664c955b54561fc36ef36 \ + --hash=sha256:32c68ef735dbe5857c810328cb2481e24722a59a2003018885514d4c09af9743 \ + --hash=sha256:3686dffb02459559c74dd3d81748269ffb0eb027c39a6fc99502de37d501faa8 \ + --hash=sha256:582215a0e9adbe0e379761260553ba11c58943e4bbe9c36430c4ca6ac74b15ed \ + --hash=sha256:5b50bf3f55561dac5438f8e70bfcdfd74543fd60df5fa5f62d94e5867deca684 \ + --hash=sha256:5bf44d66cdf9e893637896c7faa22298baebcd18d1ddb6d2626a6e39793a1d56 \ + --hash=sha256:6602bc8dc6f3a9e02b6c22c4fc1e47aa50f8f8e6d3f78a5e16ac33ef5fefa324 \ + --hash=sha256:673739cb539f8cdaa07d92d02efa93c9ccf87e345b9a0b556e3ecc666718468d \ + --hash=sha256:68678abf380b42ce21a5f2abde8efee05c114c2fdb2e9eef2efdb0257fba1235 \ + --hash=sha256:68e7c44931cc171c54ccb702482e9fc723192e88d25a0e133edd7aff8fcd1f6e \ + --hash=sha256:6b3d6606d369fc1da4fd8c357d026317fbb9c9b75d36dc16e90e84c26854b088 \ + --hash=sha256:748dcd1e3d3d7cd5443ef03ce8685043294ad6bd7c02a38d1bd367cfd968e000 \ + --hash=sha256:7651c50c8c5ef7bdb41108b7b8c5a83013bfaa8a935590c5d74627c047a583c7 \ + --hash=sha256:7b78010e7b97fef4bee1e896df8a4bbb6712b7f05b7ef630f9d1da00f6444d2e \ + --hash=sha256:7e61e3e4fa664a8588aa25c883eab612a188c725755afff6289454d6362b9673 \ + --hash=sha256:80876338e19c951fdfed6198e70bc88f1c9758b94578d5a7c4c91a87af3cf31c \ + --hash=sha256:8895613bcc094d4a1b2dbe179d88d7fb4a15cee43c052e8885783fac397d91fe \ + --hash=sha256:88e2b3c14bdb32e440be531ade29d3c50a1a59cd4e51b1dd8b0865c54ea5d2e2 \ + --hash=sha256:8f8e709127c6c77446a8c0a8c8bf3c8ee706a06cd44b1e827c3e6a2ee6b8c098 \ + --hash=sha256:9cb4a35b3642fc5c005a6755a5d17c6c8b6bcb6981baf81cea8bfbc8903e8ba8 \ + --hash=sha256:9f90389693731ff1f659e55c7d1640e2ec43ff725cc61b04b2f9c6d8d017df6a \ + --hash=sha256:a09582f178759ee8128d9270cd1344154fd473bb77d94ce0aeb2a93ebf0feaf0 \ + --hash=sha256:a6a14b17d7e17fa0d207ac08642c8820f84f25ce17a442fd15e27ea18d67c59b \ + --hash=sha256:a72e8961a86d19bdb45851d8f1f08b041ea37d2bd8d4fd19903bc3083d80c896 \ + --hash=sha256:abd808f9c129ba2beda4cfc53bde801e5bcf9d6e0f22f095e45327c038bfe68e \ + --hash=sha256:ac0f5edd2360eea2f1daa9e26a41db02dd4b0451b48f7c318e217ee092a213e9 \ + --hash=sha256:b29ebffcf550f9da55bec9e02ad430c992a87e5f512cd63388abb76f1036d8d2 \ + --hash=sha256:b2ca4e77f9f47c55c194982e10f058db063937845bb2b7a86c84a6cfe0aefa8b \ + --hash=sha256:b7be2d771cdba2942e13215c4e340bfd76398e9227ad10402a8767ab1865d2e6 \ + --hash=sha256:b84834d0cf97e7d27dd5b7f3aca7b6e9263c56308ab9dc8aae9784abb774d404 \ + --hash=sha256:b86851a328eedc692acf81fb05444bdf1891747c25af7529e39ddafaf68a4f3f \ + --hash=sha256:bcb3ef43e58665bbda2fb198698fcae6776483e0c4a631aa5647806c25e02cc0 \ + --hash=sha256:c0f31130ebc2d37cdd8e44605fb5fa7ad59049298b3f745c74fa74c62fbfcfc4 \ + --hash=sha256:c6a164aa47843fb1b01e941d385aab7215563bb8816d80ff3a363a9f8448a8dc \ + --hash=sha256:d8a9d3ebe49f084ad71f9269834ceccbf398253c9fac910c4fd7053ff1386936 \ + --hash=sha256:db8e577c19c0fda0beb7e0d4e09e0ba74b1e4c092e0e40bfa12fe05b6f6d75ba \ + --hash=sha256:dc9b18bf40cc75f66f40a7379f6a9513244fe33c0e8aa72e2d56b0196a7ef872 \ + --hash=sha256:e09f3ff613345df5e8c3667da1d918f9149bd623cd9070c983c013792a9a62eb \ + --hash=sha256:e4108df7fe9b707191e55f33efbcb2d81928e10cea45527879a4749cbe472614 \ + --hash=sha256:e6024675e67af929088fda399b2094574609396b1decb609c55fa58b028a32a1 \ + --hash=sha256:e70f54f1796669ef691ca07d046cd81a29cb4deb1e5f942003f401c0c4a2695d \ + --hash=sha256:e715596e683d2ce000574bae5d07bd522c781a822866c20495e52520564f0969 \ + --hash=sha256:e760191dd42581e023a68b758769e2da259b5d52e3103c6060ddc02c9edb8d7b \ + --hash=sha256:ed86a35631f7bfbb28e108dd96773b9d5a6ce4811cf6ea468bb6a359b256b1e4 \ + --hash=sha256:ee07e47c12890ef248766a6e55bd38ebfb2bb8edd4142d56db91b21ea68b7627 \ + --hash=sha256:fa3a0128b152627161ce47201262d3140edb5a5c3da88d73a1b790a959126956 \ + --hash=sha256:fcc8eb6d5902bb1cf6dc4f187ee3ea80a1eba0a89aba40a5cb20a5087d961357 # via cryptography -chardet==3.0.4 \ - --hash=sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae \ - --hash=sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691 - # via -r requirements.in charset-normalizer==2.0.12 \ --hash=sha256:2857e29ff0d34db842cd7ca3230549d1a697f96ee6d3fb071cfa6c7393832597 \ --hash=sha256:6881edbebdb17b39b4eaaa821b438bf6eddffb4468cf344f09f89def34a8b1df # via requests -click==8.1.1 \ - --hash=sha256:5e0d195c2067da3136efb897449ec1e9e6c98282fbf30d7f9e164af9be901a6b \ - --hash=sha256:7ab900e38149c9872376e8f9b5986ddcaf68c0f413cf73678a0bca5547e6f976 +click==8.1.7 \ + --hash=sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28 \ + --hash=sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de # via # -r requirements.in # flask @@ -119,45 +134,51 @@ cryptoadvance-liquidissuer==0.2.4 \ --hash=sha256:5a2c531801854c5a4a46daf184877e22f731cdb42d2cfb840785bda7371ba6fb \ --hash=sha256:9e468f3e35ecc566b3f74a2263677cf26632548abb194521dba15ad37acd1e9b # via -r requirements.in -cryptoadvance-spectrum==0.6.4 \ - --hash=sha256:226f89dfe96dac2fcd82f462706e118f017582df5565ff1924f27e4392bc6a0e \ - --hash=sha256:fa6c877b3a5ba683ccde991f020fce2f3b06e0b8f5219a6fdc592658cacb1d3d +cryptoadvance-spectrum==0.6.7 \ + --hash=sha256:b9b2350a04b5b33d383939ae284a8f3ff1fab80e1eb5069b710f767d3e7ead52 # via -r requirements.in -cryptography==39.0.1 \ - --hash=sha256:0f8da300b5c8af9f98111ffd512910bc792b4c77392a9523624680f7956a99d4 \ - --hash=sha256:35f7c7d015d474f4011e859e93e789c87d21f6f4880ebdc29896a60403328f1f \ - --hash=sha256:4789d1e3e257965e960232345002262ede4d094d1a19f4d3b52e48d4d8f3b885 \ - --hash=sha256:5aa67414fcdfa22cf052e640cb5ddc461924a045cacf325cd164e65312d99502 \ - --hash=sha256:5d2d8b87a490bfcd407ed9d49093793d0f75198a35e6eb1a923ce1ee86c62b41 \ - --hash=sha256:6687ef6d0a6497e2b58e7c5b852b53f62142cfa7cd1555795758934da363a965 \ - --hash=sha256:6f8ba7f0328b79f08bdacc3e4e66fb4d7aab0c3584e0bd41328dce5262e26b2e \ - --hash=sha256:706843b48f9a3f9b9911979761c91541e3d90db1ca905fd63fee540a217698bc \ - --hash=sha256:807ce09d4434881ca3a7594733669bd834f5b2c6d5c7e36f8c00f691887042ad \ - --hash=sha256:83e17b26de248c33f3acffb922748151d71827d6021d98c70e6c1a25ddd78505 \ - --hash=sha256:96f1157a7c08b5b189b16b47bc9db2332269d6680a196341bf30046330d15388 \ - --hash=sha256:aec5a6c9864be7df2240c382740fcf3b96928c46604eaa7f3091f58b878c0bb6 \ - --hash=sha256:b0afd054cd42f3d213bf82c629efb1ee5f22eba35bf0eec88ea9ea7304f511a2 \ - --hash=sha256:c5caeb8188c24888c90b5108a441c106f7faa4c4c075a2bcae438c6e8ca73cef \ - --hash=sha256:ced4e447ae29ca194449a3f1ce132ded8fcab06971ef5f618605aacaa612beac \ - --hash=sha256:d1f6198ee6d9148405e49887803907fe8962a23e6c6f83ea7d98f1c0de375695 \ - --hash=sha256:e124352fd3db36a9d4a21c1aa27fd5d051e621845cb87fb851c08f4f75ce8be6 \ - --hash=sha256:e422abdec8b5fa8462aa016786680720d78bdce7a30c652b7fadf83a4ba35336 \ - --hash=sha256:ef8b72fa70b348724ff1218267e7f7375b8de4e8194d1636ee60510aae104cd0 \ - --hash=sha256:f0c64d1bd842ca2633e74a1a28033d139368ad959872533b1bab8c80e8240a0c \ - --hash=sha256:f24077a3b5298a5a06a8e0536e3ea9ec60e4c7ac486755e5fb6e6ea9b3500106 \ - --hash=sha256:fdd188c8a6ef8769f148f88f859884507b954cc64db6b52f66ef199bb9ad660a \ - --hash=sha256:fe913f20024eb2cb2f323e42a64bdf2911bb9738a15dba7d3cce48151034e3a8 +cryptography==42.0.7 \ + --hash=sha256:02c0eee2d7133bdbbc5e24441258d5d2244beb31da5ed19fbb80315f4bbbff55 \ + --hash=sha256:0d563795db98b4cd57742a78a288cdbdc9daedac29f2239793071fe114f13785 \ + --hash=sha256:16268d46086bb8ad5bf0a2b5544d8a9ed87a0e33f5e77dd3c3301e63d941a83b \ + --hash=sha256:1a58839984d9cb34c855197043eaae2c187d930ca6d644612843b4fe8513c886 \ + --hash=sha256:2954fccea107026512b15afb4aa664a5640cd0af630e2ee3962f2602693f0c82 \ + --hash=sha256:2e47577f9b18723fa294b0ea9a17d5e53a227867a0a4904a1a076d1646d45ca1 \ + --hash=sha256:31adb7d06fe4383226c3e963471f6837742889b3c4caa55aac20ad951bc8ffda \ + --hash=sha256:3577d029bc3f4827dd5bf8bf7710cac13527b470bbf1820a3f394adb38ed7d5f \ + --hash=sha256:36017400817987670037fbb0324d71489b6ead6231c9604f8fc1f7d008087c68 \ + --hash=sha256:362e7197754c231797ec45ee081f3088a27a47c6c01eff2ac83f60f85a50fe60 \ + --hash=sha256:3de9a45d3b2b7d8088c3fbf1ed4395dfeff79d07842217b38df14ef09ce1d8d7 \ + --hash=sha256:4f698edacf9c9e0371112792558d2f705b5645076cc0aaae02f816a0171770fd \ + --hash=sha256:5482e789294854c28237bba77c4c83be698be740e31a3ae5e879ee5444166582 \ + --hash=sha256:5e44507bf8d14b36b8389b226665d597bc0f18ea035d75b4e53c7b1ea84583cc \ + --hash=sha256:779245e13b9a6638df14641d029add5dc17edbef6ec915688f3acb9e720a5858 \ + --hash=sha256:789caea816c6704f63f6241a519bfa347f72fbd67ba28d04636b7c6b7da94b0b \ + --hash=sha256:7f8b25fa616d8b846aef64b15c606bb0828dbc35faf90566eb139aa9cff67af2 \ + --hash=sha256:8cb8ce7c3347fcf9446f201dc30e2d5a3c898d009126010cbd1f443f28b52678 \ + --hash=sha256:93a3209f6bb2b33e725ed08ee0991b92976dfdcf4e8b38646540674fc7508e13 \ + --hash=sha256:a3a5ac8b56fe37f3125e5b72b61dcde43283e5370827f5233893d461b7360cd4 \ + --hash=sha256:a47787a5e3649008a1102d3df55424e86606c9bae6fb77ac59afe06d234605f8 \ + --hash=sha256:a79165431551042cc9d1d90e6145d5d0d3ab0f2d66326c201d9b0e7f5bf43604 \ + --hash=sha256:a987f840718078212fdf4504d0fd4c6effe34a7e4740378e59d47696e8dfb477 \ + --hash=sha256:a9bc127cdc4ecf87a5ea22a2556cab6c7eda2923f84e4f3cc588e8470ce4e42e \ + --hash=sha256:bd13b5e9b543532453de08bcdc3cc7cebec6f9883e886fd20a92f26940fd3e7a \ + --hash=sha256:c65f96dad14f8528a447414125e1fc8feb2ad5a272b8f68477abbcc1ea7d94b9 \ + --hash=sha256:d8e3098721b84392ee45af2dd554c947c32cc52f862b6a3ae982dbb90f577f14 \ + --hash=sha256:e6b79d0adb01aae87e8a44c2b64bc3f3fe59515280e00fb6d57a7267a2583cda \ + --hash=sha256:e6b8f1881dac458c34778d0a424ae5769de30544fc678eac51c1c8bb2183e9da \ + --hash=sha256:e9b2a6309f14c0497f348d08a065d52f3020656f675819fc405fb63bbcd26562 \ + --hash=sha256:ecbfbc00bf55888edda9868a4cf927205de8499e7fabe6c050322298382953f2 \ + --hash=sha256:efd0bf5205240182e0f13bcaea41be4fdf5c22c5129fc7ced4a0282ac86998c9 # via # -r requirements.in # noiseprotocol # pgpy # pyopenssl -ecdsa==0.18.0 \ - --hash=sha256:190348041559e21b22a1d65cee485282ca11a6f81d503fddb84d5017e9ed1e49 \ - --hash=sha256:80600258e7ed2f16b9aa1d7c295bd70194109ad5a30fdee0eaeefef1d4c559dd - # via - # bitbox02 - # hwi +ecdsa==0.19.0 \ + --hash=sha256:2cea9b88407fdac7bbeca0833b189e4c9c53f2ef1e1eaa29f6224dbc809b707a \ + --hash=sha256:60eaad1199659900dd0af521ed462b793bbdf867432b3948e87416ae4caf6bf8 + # via hwi embit==0.6.1 \ --hash=sha256:16a84c6668dc9ffc907594457a46f7142cee379646bc009a5a9b77b0d2cb4e12 # via @@ -204,9 +225,9 @@ flask-sqlalchemy==2.5.1 \ --hash=sha256:2bda44b43e7cacb15d4e05ff3cc1f8bc97936cc464623424102bfc2c35e95912 \ --hash=sha256:f12c3d4cc5cc7fdcc148b9527ea05671718c3ea45d50c7e732cceb33f574b390 # via cryptoadvance-spectrum -flask-wtf==0.15.1 \ - --hash=sha256:6ff7af73458f182180906a37a783e290bdc8a3817fe4ad17227563137ca285bf \ - --hash=sha256:ff177185f891302dc253437fe63081e7a46a4e99aca61dfe086fb23e54fff2dc +flask-wtf==1.2.1 \ + --hash=sha256:8bb269eb9bb46b87e7c8233d7e7debdf1f8b74bf90cc1789988c29b37a97b695 \ + --hash=sha256:fa6793f2fb7e812e0fe9743b282118e581fb1b6c45d414b8af05e659bd653287 # via -r requirements.in greenlet==2.0.2 \ --hash=sha256:03a8f4f3430c3b3ff8d10a2a86028c660355ab637cee9333d63d66b56f09d52a \ @@ -282,68 +303,94 @@ h11==0.14.0 \ --hash=sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d \ --hash=sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761 # via wsproto -hidapi==0.13.1 \ - --hash=sha256:0933fef6e1599c38093bec22a3dded359fb0a9ce57ab43db8947eca7308fe0a8 \ - --hash=sha256:0c6ae8a0b69ca9ae0ad3f948e1174231a9b781a70ff3642e6b110dfec770cf59 \ - --hash=sha256:0e245b5177fc59548e4f76400a07a7882fca48a1c650f7c5d902afe519869bd0 \ - --hash=sha256:155bce0c0741ee3b4cf93ac53d02526ef16008a1ef3d9071cd67dd4e190c02d8 \ - --hash=sha256:1b2421ace55d1cca96b8d5c09f918e7662b6ac7b7dbb0f624814e831dab5062e \ - --hash=sha256:1eb1247930ebf45275937db011856880dc6e3c961290dc8783835230312250b6 \ - --hash=sha256:20c06a2256696e83c5f2789dcd9a8109009af5f3e75af0082934867fc6de5283 \ - --hash=sha256:22d5af6f776fffce8078d6d3785dc8c1647688183c1dbda24acf6dd841d76ac3 \ - --hash=sha256:2dcaf4b9df91a10c0e2904f175c0bfed53d51a3fe73d2aba7061ddc360249f86 \ - --hash=sha256:2f013d7e107aef90a4c79802f7e44f5280f9fd62d3d43e307dd7d108c95277cd \ - --hash=sha256:3c190d4f8be403c8ee543684013e85494ac690da2d636a03a4f7f5b75b41bba5 \ - --hash=sha256:3cb651134546cabc1e515b38626732770ec3ab0157b33dc132fc2f4607858658 \ - --hash=sha256:5696e1479c203e6123db5f6c5effe6fdca3a5da332bd9ba559687056af7b1d3c \ - --hash=sha256:571e3a07bcbdc13c2d2af9bd09839e6f45cfbac8d5e2d28b7e1b844427813162 \ - --hash=sha256:58580360dc69206a54efccc6d234baa6dbfa56394386ff77bdd67228b27c9759 \ - --hash=sha256:5a51521200fecd677695d7bd12a6efdccf315a16929a65b836fb7ec8596a24f4 \ - --hash=sha256:63494ccce46bf60491d7e44f1371443a178e21afa287b341288057bfbefbe872 \ - --hash=sha256:6490021f7d8227fb6277355f143539404c1b9074d77d64febbe0d4a629f0835b \ - --hash=sha256:64ec0dcce28232354a91d4575a74eab77fcbb4379fa2f4650e9eaadd7561d887 \ - --hash=sha256:68b9281c22e7832e1d140d3eb65718486b61ac24701837c0ff211f2195bedfb8 \ - --hash=sha256:6ab4dd2e19918beff50638be89b9626191df4514456fd7388611762cdbbd46cc \ - --hash=sha256:6c0a285b3720d1277f318b574d987483d6127b75d7d76decdd6c85c724af8ef7 \ - --hash=sha256:7b59778cec7ef9f6151778bf65a1b1dd164014238d7d4345c8e376c4a4baac0e \ - --hash=sha256:8174e095386a37eb3d8eb08e942f3aa7e6bd703c296dbb6c45cbca40862a7253 \ - --hash=sha256:871808f5b4c8049f758b94375e24bfe87c8a58f91f7c2ca4c482ab003c9af9a7 \ - --hash=sha256:8a735ea49c68d6008b4e07b3de5b4c211f875c262dd14d6669a3629bc482a375 \ - --hash=sha256:93b95d8bb6c87f6db03b6eb9faffb2fe977ac788791898651826c1b61a4734c1 \ - --hash=sha256:93dfc2739e0d3f773ae148fed9c0f020c13e17d9d512cf73b54a5e60f0b28c68 \ - --hash=sha256:99b18b28ec414ef9b604ddaed08182e486a400486f31ca56f61d537eed1d17cf \ - --hash=sha256:9ed536627429bc299b28188a28d2fe4230b472d737ee9dc5c8376fd66c418674 \ - --hash=sha256:a3fcfe6e4a75651baf704001ddf765326ddd64b55b1ba6cd0e2e0851409c27c4 \ - --hash=sha256:a48a0c3f1113ac05d389ae4eff00485a253c63dc6e7c7c17ae3026d063ca809a \ - --hash=sha256:aca9899e7fccc6f9e6a909681c5d97cc2ba224683d886c96bcdca3165454d875 \ - --hash=sha256:afc7991c12235f3392b5f744b75878429dbf5a3a4a0d51ca18b1e82ebcce7033 \ - --hash=sha256:c8647eeb286d1b46104a297258b4af15e7878b3dbdb9951ba01cf233b2ff513a \ - --hash=sha256:cf6a90f988c3a1b1910f441c29e4c8c21150c6cb0ed5a8be3bea715aed02002d \ - --hash=sha256:d1e91ef0cca8185a858491d9e253f585e58888c5350013ab9305caf4b7f563ba \ - --hash=sha256:da551cdf88122e9d7dbb93557efa69d35fb1b1fea0510a5bd461af331ad92892 \ - --hash=sha256:ebffcf48798bbf2018a86f1465eeabce0da98c2066c99a121db97dd397b798b3 \ - --hash=sha256:ec97dcb6697659be0878aac14143ba00ab43e86a6bbec2fa6af46f833ab69743 \ - --hash=sha256:f5a16a8a97ac944775369713b35bb47a7cb6b3091458e7cfe4fdec3f7ac9c29e - # via - # bitbox02 - # hwi -hwi==2.1.1 \ - --hash=sha256:81ebb2917291c3d2ef3252f44aa141ffb8d74f0c2abfa2a98410800426c20e3f \ - --hash=sha256:fa32223b9c26636b46eef9b5602a3d259f011f8490ae68dfb5812a5f9563d084 +hidapi==0.14.0 \ + --hash=sha256:01929fbbe206ebcb0bad9b8e925e16de0aa8f872bf80a263f599e519866d9900 \ + --hash=sha256:0fb47a0a8c3a6797306ea9eb8d1bdad68e5493ef5c8fa2e644501d56f2677551 \ + --hash=sha256:103dfa1c19832b8928775ec491c3016c9f9063dd2ccdc37811bf12f3cc0a789f \ + --hash=sha256:1370bc6a364fd292accd580a8d7bac4219932144d149f3a513bb472581eac421 \ + --hash=sha256:15f1fd34b0719d1e4d1bbc0bce325b318ee3e85c36fac0d23c6fb9d7f4d611db \ + --hash=sha256:174be08584e5c686fb02a6f51cc159d6e491fd7a7c7d1978b28f913362c7ad11 \ + --hash=sha256:1a63c0bc33329d0e572afe20b9dff27155d4fff34d0f2fa662e6704b9e2e18c4 \ + --hash=sha256:1a777912e93a9f773aa6359fdb7b152b654991bb9afd6d3ce20e52dfbf18db00 \ + --hash=sha256:1b4052f17321f5f0b641e020eae87db5bb0103f893198e61b2495358db83ddab \ + --hash=sha256:1c0959d89bc95acb4f9e6d58c8562281e22694959e42c10108193a1362b4fcd9 \ + --hash=sha256:2906ad143ec40009c33348ab4b3f7a9bdaa87b65bdc55983399bed47ee90a818 \ + --hash=sha256:2e635c037d28e1ceded2043d81b879d81348a278d1ae668954a5a7a7d383f7d7 \ + --hash=sha256:33098ba2f10f704a85b62720becf444a19753d3a1ee4b8dda7dc289c1d6eda9b \ + --hash=sha256:349976417f7f3371c7133a6427ed8f4faa06fbd93e9b5309d86689f25f191150 \ + --hash=sha256:365d7c9fdcae71ae41797dc2dd062dfed4362d1b36d21fa62afbc16c5ec3cd5a \ + --hash=sha256:3b8af9ef71b7149e85f2118eaac9fd7e7ea95528029a66f351d0049877d5a179 \ + --hash=sha256:3ed9f993a6f8a611c11ef213968c6972e17d7e8b27936349884c475dc0309e71 \ + --hash=sha256:4046bbfc67c5587ca638b875858569a8787e6955eff5dea4e424044de09fe7e4 \ + --hash=sha256:418de0a2ec786d610967984fe5d6cb9584413dcce8b9fdd23fff998596f80a95 \ + --hash=sha256:48e2cf77626f3cfdda9624de3be7f9c55e37efbb39882d2e96a92d38893a09cb \ + --hash=sha256:4b65cc159fcf1839d078d3de196146626c1a865bd9136fda5fa490f689e904c9 \ + --hash=sha256:4c78ff5c46128bdf68b2c4e4b08fac7765ef79f6ee7e17c8a2f7d3090a591d97 \ + --hash=sha256:5299d74d96bdc9eaa83496c972048db0027d012a08440b33bdb6dd10a7491da9 \ + --hash=sha256:537fc17d59e1de48c1832d5bda60d63f56bcb1300cce7e382d45b8ef3bcacd53 \ + --hash=sha256:5b960bcf8c41bd861554adc5932d1d7e0ed169315ca87dbd4d23ec8337764247 \ + --hash=sha256:5e3318f0e66c4d46977fc8ba73a2ad33c2de367d133b70b243051283d0ecdaca \ + --hash=sha256:60c034ec3ef3e5679232d9e6c003c4848e4772032d683f0b91ddb84b87d8698d \ + --hash=sha256:651c2382e974e866d78334cdde3c290a04fcbab4cec940c0d3586d77d11b9566 \ + --hash=sha256:6c000635c14f11ee3633530ef2d56de1ef266dc89b98f0a5f21e08ab8a9b151b \ + --hash=sha256:76041e2e5d52c864bc4a381f082edeb89e85829130d1fef3366f320237da0580 \ + --hash=sha256:78b176bc64a8908b37d5f34b3cce30158c1ebeaf1208c3b5ed62ad456fa1277d \ + --hash=sha256:7ef0f40a02e0b56fe2e7c93dfc9810245f2feeaa0c2ea76654d0768722883639 \ + --hash=sha256:810ad22831e4a150c2d6f27141fcf2826fd085ccacf4262d5c742c90aa81cd54 \ + --hash=sha256:832a2d2d8509d98381f0bf09b4e1f897765a9c8e0a72164174bcbf983d7d69a3 \ + --hash=sha256:833a32c3e44780f37d46dffd559b8e245034c92ae25060f752e4f34e9c7efe24 \ + --hash=sha256:86752ca0db00e5a5e991ebc5854400ff16d3812d6d9a156fea4de7d5f10ba801 \ + --hash=sha256:93697007df8ba38ab3ae3e777a6875cd1775fc720afe27e4c624cecbab7720de \ + --hash=sha256:93d7814aa1c7e0f1cce300b3b63828abecb024da72e9a10d46db811cf466e68e \ + --hash=sha256:96ecea60915212e59940db41c2a91709ebd4ec6a04e03b0db37a4ddb6825bee6 \ + --hash=sha256:9ab1b1dc8b915a0faa7b976ed8291142cf93c2acecf533db8c748fc64be1a004 \ + --hash=sha256:9e245719a5ede83c779dd99a4553002ae684d92d0f3e4274dcf06882b063f127 \ + --hash=sha256:9fdc08eb19f2fffb989124d1dbea3aa62dd0036615bbf464ceafee0353673bf4 \ + --hash=sha256:a68820a5de54a54d145d88f31c74257965bd03ae454263eda054f02bf34dcc9c \ + --hash=sha256:a6edc57962a9f30bff73fc0cc80915c9da9ab3e0892c601263198f8d21d8dfff \ + --hash=sha256:a7cb029286ced5426a381286526d9501846409701a29c2538615c3d1a612b8be \ + --hash=sha256:b054abf40b5aa7122314af59d0244fa274a50c4276d20695d8b7ff69564beb95 \ + --hash=sha256:b264c6a1a1a0cacacc82299785415bec91184cb3e4a77d127c40016086705327 \ + --hash=sha256:b4513311fad7e499ebb0d7a26178557b85044983199a280cb95c2038902fe1a0 \ + --hash=sha256:b4a0feac62d80eca36e2c8035fe4f57c440fbfcd9273a909112cb5bd9baae449 \ + --hash=sha256:b4f3a4e41886a19dcb9ea872a6f75ef42baba124a150b5b0a03379da174e1f70 \ + --hash=sha256:bb87cf8f23c15346bc1487e6f39d11b37d3ff7788037d3760b7907ea325b6d2c \ + --hash=sha256:c1425f523258d25d8f32a6493978532477c4d7507f5f9252417b1d629427871e \ + --hash=sha256:c1b1ded4a823cc5c2075a622b48d02bc0a72f57579ea24c956ef29649a49eb66 \ + --hash=sha256:c8bba64d6ed49fa7ea4f4515986450223f5c744be448c846fb0614bc53b536bd \ + --hash=sha256:cc654cd37d04bbb782c39901bf872b2af5d3c3ead2b1a23b084a81e469b6d0a7 \ + --hash=sha256:de293e7291b1ec813a97e42625c2c0a41b0d25d495b3dc5864bbb3dbbb5a719d \ + --hash=sha256:de5af3941f31cfb044a87fc9d9b2f80b3b71b58b27481d9877061b76e9625a22 \ + --hash=sha256:dff930adb37d1bcaeca3cf0dcec00eb72c109aa42c84858809cbae2972d79661 \ + --hash=sha256:e1927fc5f7099b98529a4cefe8e0cd92ffb026abf5c449310d1d359433c5d94a \ + --hash=sha256:e43f2db347b7faf3fcefb6c39f45615d1d6f58db7305d4474bb63b2845ed4fc8 \ + --hash=sha256:e5f01e21648a58de56c24a093e4901fca039b9658074b413c2a4ceb16ea6473b \ + --hash=sha256:e6ef0bdc69310cfdff83faf96c75492ac3d8cf355af275904f1dd90a3c5f24a4 \ + --hash=sha256:e7ff737cbb4adf238aa0da50e8b5c2f083e8f62b3c5132fbd732ba59918a909c \ + --hash=sha256:e822e899c13eb1e3a575712d7be5bd03a9103f6027b00ab4351c8404cec5719d \ + --hash=sha256:ed112c9ba0adf41d7e04bf5389dc150ada4d94a6ef1cb56c325d5aed1e4e07d2 \ + --hash=sha256:f575381efa788e1a894c68439644817b152b8a68ead643e42c23ba28eeedc33b \ + --hash=sha256:f68bbf88805553911e7e5a9b91136c96a54042b6e3d82d39d733d2edb46ff9a6 \ + --hash=sha256:fb4e94e45f6dddb20d59501187721e5d3b02e6cc8a59d261dd5cac739008582a \ + --hash=sha256:fc9ec2321bf3b0b4953910aa87c0c8ab5f93b1f113a9d3d4f18845ce54708d13 + # via hwi +hwi==2.4.0 \ + --hash=sha256:3eaa7593f1ab360569eacdd9507dab75532bb58e8cd991d8ad72f5c4fcb67997 \ + --hash=sha256:7cb7ef2a4db4bc434815374d9bad43c6425491f77828314a2d2898d3e86d3f04 # via -r requirements.in -idna==3.4 \ - --hash=sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4 \ - --hash=sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2 +idna==3.7 \ + --hash=sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc \ + --hash=sha256:82fee1fc78add43492d3a1898bfa6d8a904cc97d8427f683ed8e798d07761aa0 # via requests -itsdangerous==2.1.2 \ - --hash=sha256:2c2349112351b88699d8d4b6b075022c0808887cb7ad10069318a8b0bc88db44 \ - --hash=sha256:5dbbc68b317e5e42f327f9021763545dc3fc3bfe22e6deb96aaf1fc38874156a +itsdangerous==2.2.0 \ + --hash=sha256:c6242fc49e35958c8b15141343aa660db5fc54d4f13a1db01a3f5891b98700ef \ + --hash=sha256:e0050c0b7da1eea53ffaf149c0cfbb5c6e2e2b69c4bef22c81fa6eb73e5f6173 # via # flask # flask-wtf -jinja2==3.1.2 \ - --hash=sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852 \ - --hash=sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61 +jinja2==3.1.4 \ + --hash=sha256:4a3aee7acbbe7303aede8e9648d13b8bf88a429282aa6122a993f0ac800cb369 \ + --hash=sha256:bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d # via # flask # flask-babel @@ -353,129 +400,149 @@ libusb1==2.0.1 \ --hash=sha256:a97bcb90f589d863c5e971b013c8cf7e1915680a951e66c4222a2c5bb64b7153 \ --hash=sha256:d3ba82ecf7ab6a48d21dac6697e26504670cc3522b8e5941bd28fb56cf3f6c46 # via hwi -markupsafe==2.1.2 \ - --hash=sha256:0576fe974b40a400449768941d5d0858cc624e3249dfd1e0c33674e5c7ca7aed \ - --hash=sha256:085fd3201e7b12809f9e6e9bc1e5c96a368c8523fad5afb02afe3c051ae4afcc \ - --hash=sha256:090376d812fb6ac5f171e5938e82e7f2d7adc2b629101cec0db8b267815c85e2 \ - --hash=sha256:0b462104ba25f1ac006fdab8b6a01ebbfbce9ed37fd37fd4acd70c67c973e460 \ - --hash=sha256:137678c63c977754abe9086a3ec011e8fd985ab90631145dfb9294ad09c102a7 \ - --hash=sha256:1bea30e9bf331f3fef67e0a3877b2288593c98a21ccb2cf29b74c581a4eb3af0 \ - --hash=sha256:22152d00bf4a9c7c83960521fc558f55a1adbc0631fbb00a9471e097b19d72e1 \ - --hash=sha256:22731d79ed2eb25059ae3df1dfc9cb1546691cc41f4e3130fe6bfbc3ecbbecfa \ - --hash=sha256:2298c859cfc5463f1b64bd55cb3e602528db6fa0f3cfd568d3605c50678f8f03 \ - --hash=sha256:28057e985dace2f478e042eaa15606c7efccb700797660629da387eb289b9323 \ - --hash=sha256:2e7821bffe00aa6bd07a23913b7f4e01328c3d5cc0b40b36c0bd81d362faeb65 \ - --hash=sha256:2ec4f2d48ae59bbb9d1f9d7efb9236ab81429a764dedca114f5fdabbc3788013 \ - --hash=sha256:340bea174e9761308703ae988e982005aedf427de816d1afe98147668cc03036 \ - --hash=sha256:40627dcf047dadb22cd25ea7ecfe9cbf3bbbad0482ee5920b582f3809c97654f \ - --hash=sha256:40dfd3fefbef579ee058f139733ac336312663c6706d1163b82b3003fb1925c4 \ - --hash=sha256:4cf06cdc1dda95223e9d2d3c58d3b178aa5dacb35ee7e3bbac10e4e1faacb419 \ - --hash=sha256:50c42830a633fa0cf9e7d27664637532791bfc31c731a87b202d2d8ac40c3ea2 \ - --hash=sha256:55f44b440d491028addb3b88f72207d71eeebfb7b5dbf0643f7c023ae1fba619 \ - --hash=sha256:608e7073dfa9e38a85d38474c082d4281f4ce276ac0010224eaba11e929dd53a \ - --hash=sha256:63ba06c9941e46fa389d389644e2d8225e0e3e5ebcc4ff1ea8506dce646f8c8a \ - --hash=sha256:65608c35bfb8a76763f37036547f7adfd09270fbdbf96608be2bead319728fcd \ - --hash=sha256:665a36ae6f8f20a4676b53224e33d456a6f5a72657d9c83c2aa00765072f31f7 \ - --hash=sha256:6d6607f98fcf17e534162f0709aaad3ab7a96032723d8ac8750ffe17ae5a0666 \ - --hash=sha256:7313ce6a199651c4ed9d7e4cfb4aa56fe923b1adf9af3b420ee14e6d9a73df65 \ - --hash=sha256:7668b52e102d0ed87cb082380a7e2e1e78737ddecdde129acadb0eccc5423859 \ - --hash=sha256:7df70907e00c970c60b9ef2938d894a9381f38e6b9db73c5be35e59d92e06625 \ - --hash=sha256:7e007132af78ea9df29495dbf7b5824cb71648d7133cf7848a2a5dd00d36f9ff \ - --hash=sha256:835fb5e38fd89328e9c81067fd642b3593c33e1e17e2fdbf77f5676abb14a156 \ - --hash=sha256:8bca7e26c1dd751236cfb0c6c72d4ad61d986e9a41bbf76cb445f69488b2a2bd \ - --hash=sha256:8db032bf0ce9022a8e41a22598eefc802314e81b879ae093f36ce9ddf39ab1ba \ - --hash=sha256:99625a92da8229df6d44335e6fcc558a5037dd0a760e11d84be2260e6f37002f \ - --hash=sha256:9cad97ab29dfc3f0249b483412c85c8ef4766d96cdf9dcf5a1e3caa3f3661cf1 \ - --hash=sha256:a4abaec6ca3ad8660690236d11bfe28dfd707778e2442b45addd2f086d6ef094 \ - --hash=sha256:a6e40afa7f45939ca356f348c8e23048e02cb109ced1eb8420961b2f40fb373a \ - --hash=sha256:a6f2fcca746e8d5910e18782f976489939d54a91f9411c32051b4aab2bd7c513 \ - --hash=sha256:a806db027852538d2ad7555b203300173dd1b77ba116de92da9afbc3a3be3eed \ - --hash=sha256:abcabc8c2b26036d62d4c746381a6f7cf60aafcc653198ad678306986b09450d \ - --hash=sha256:b8526c6d437855442cdd3d87eede9c425c4445ea011ca38d937db299382e6fa3 \ - --hash=sha256:bb06feb762bade6bf3c8b844462274db0c76acc95c52abe8dbed28ae3d44a147 \ - --hash=sha256:c0a33bc9f02c2b17c3ea382f91b4db0e6cde90b63b296422a939886a7a80de1c \ - --hash=sha256:c4a549890a45f57f1ebf99c067a4ad0cb423a05544accaf2b065246827ed9603 \ - --hash=sha256:ca244fa73f50a800cf8c3ebf7fd93149ec37f5cb9596aa8873ae2c1d23498601 \ - --hash=sha256:cf877ab4ed6e302ec1d04952ca358b381a882fbd9d1b07cccbfd61783561f98a \ - --hash=sha256:d9d971ec1e79906046aa3ca266de79eac42f1dbf3612a05dc9368125952bd1a1 \ - --hash=sha256:da25303d91526aac3672ee6d49a2f3db2d9502a4a60b55519feb1a4c7714e07d \ - --hash=sha256:e55e40ff0cc8cc5c07996915ad367fa47da6b3fc091fdadca7f5403239c5fec3 \ - --hash=sha256:f03a532d7dee1bed20bc4884194a16160a2de9ffc6354b3878ec9682bb623c54 \ - --hash=sha256:f1cd098434e83e656abf198f103a8207a8187c0fc110306691a2e94a78d0abb2 \ - --hash=sha256:f2bfb563d0211ce16b63c7cb9395d2c682a23187f54c3d79bfec33e6705473c6 \ - --hash=sha256:f8ffb705ffcf5ddd0e80b65ddf7bed7ee4f5a441ea7d3419e861a12eaf41af58 +markupsafe==2.1.5 \ + --hash=sha256:00e046b6dd71aa03a41079792f8473dc494d564611a8f89bbbd7cb93295ebdcf \ + --hash=sha256:075202fa5b72c86ad32dc7d0b56024ebdbcf2048c0ba09f1cde31bfdd57bcfff \ + --hash=sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f \ + --hash=sha256:17b950fccb810b3293638215058e432159d2b71005c74371d784862b7e4683f3 \ + --hash=sha256:1f3fbcb7ef1f16e48246f704ab79d79da8a46891e2da03f8783a5b6fa41a9532 \ + --hash=sha256:2174c595a0d73a3080ca3257b40096db99799265e1c27cc5a610743acd86d62f \ + --hash=sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617 \ + --hash=sha256:2d2d793e36e230fd32babe143b04cec8a8b3eb8a3122d2aceb4a371e6b09b8df \ + --hash=sha256:30b600cf0a7ac9234b2638fbc0fb6158ba5bdcdf46aeb631ead21248b9affbc4 \ + --hash=sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906 \ + --hash=sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f \ + --hash=sha256:3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4 \ + --hash=sha256:3e53af139f8579a6d5f7b76549125f0d94d7e630761a2111bc431fd820e163b8 \ + --hash=sha256:4096e9de5c6fdf43fb4f04c26fb114f61ef0bf2e5604b6ee3019d51b69e8c371 \ + --hash=sha256:4275d846e41ecefa46e2015117a9f491e57a71ddd59bbead77e904dc02b1bed2 \ + --hash=sha256:4c31f53cdae6ecfa91a77820e8b151dba54ab528ba65dfd235c80b086d68a465 \ + --hash=sha256:4f11aa001c540f62c6166c7726f71f7573b52c68c31f014c25cc7901deea0b52 \ + --hash=sha256:5049256f536511ee3f7e1b3f87d1d1209d327e818e6ae1365e8653d7e3abb6a6 \ + --hash=sha256:58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169 \ + --hash=sha256:598e3276b64aff0e7b3451b72e94fa3c238d452e7ddcd893c3ab324717456bad \ + --hash=sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2 \ + --hash=sha256:5dedb4db619ba5a2787a94d877bc8ffc0566f92a01c0ef214865e54ecc9ee5e0 \ + --hash=sha256:619bc166c4f2de5caa5a633b8b7326fbe98e0ccbfacabd87268a2b15ff73a029 \ + --hash=sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f \ + --hash=sha256:656f7526c69fac7f600bd1f400991cc282b417d17539a1b228617081106feb4a \ + --hash=sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced \ + --hash=sha256:72b6be590cc35924b02c78ef34b467da4ba07e4e0f0454a2c5907f473fc50ce5 \ + --hash=sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c \ + --hash=sha256:7a68b554d356a91cce1236aa7682dc01df0edba8d043fd1ce607c49dd3c1edcf \ + --hash=sha256:7b2e5a267c855eea6b4283940daa6e88a285f5f2a67f2220203786dfa59b37e9 \ + --hash=sha256:823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb \ + --hash=sha256:8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad \ + --hash=sha256:8dd717634f5a044f860435c1d8c16a270ddf0ef8588d4887037c5028b859b0c3 \ + --hash=sha256:8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1 \ + --hash=sha256:97cafb1f3cbcd3fd2b6fbfb99ae11cdb14deea0736fc2b0952ee177f2b813a46 \ + --hash=sha256:a17a92de5231666cfbe003f0e4b9b3a7ae3afb1ec2845aadc2bacc93ff85febc \ + --hash=sha256:a549b9c31bec33820e885335b451286e2969a2d9e24879f83fe904a5ce59d70a \ + --hash=sha256:ac07bad82163452a6884fe8fa0963fb98c2346ba78d779ec06bd7a6262132aee \ + --hash=sha256:ae2ad8ae6ebee9d2d94b17fb62763125f3f374c25618198f40cbb8b525411900 \ + --hash=sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5 \ + --hash=sha256:bc1667f8b83f48511b94671e0e441401371dfd0f0a795c7daa4a3cd1dde55bea \ + --hash=sha256:bec0a414d016ac1a18862a519e54b2fd0fc8bbfd6890376898a6c0891dd82e9f \ + --hash=sha256:bf50cd79a75d181c9181df03572cdce0fbb75cc353bc350712073108cba98de5 \ + --hash=sha256:bff1b4290a66b490a2f4719358c0cdcd9bafb6b8f061e45c7a2460866bf50c2e \ + --hash=sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a \ + --hash=sha256:c8b29db45f8fe46ad280a7294f5c3ec36dbac9491f2d1c17345be8e69cc5928f \ + --hash=sha256:ce409136744f6521e39fd8e2a24c53fa18ad67aa5bc7c2cf83645cce5b5c4e50 \ + --hash=sha256:d050b3361367a06d752db6ead6e7edeb0009be66bc3bae0ee9d97fb326badc2a \ + --hash=sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b \ + --hash=sha256:d9fad5155d72433c921b782e58892377c44bd6252b5af2f67f16b194987338a4 \ + --hash=sha256:daa4ee5a243f0f20d528d939d06670a298dd39b1ad5f8a72a4275124a7819eff \ + --hash=sha256:db0b55e0f3cc0be60c1f19efdde9a637c32740486004f20d1cff53c3c0ece4d2 \ + --hash=sha256:e61659ba32cf2cf1481e575d0462554625196a1f2fc06a1c777d3f48e8865d46 \ + --hash=sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b \ + --hash=sha256:ec6a563cff360b50eed26f13adc43e61bc0c04d94b8be985e6fb24b81f6dcfdf \ + --hash=sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5 \ + --hash=sha256:fa173ec60341d6bb97a89f5ea19c85c5643c1e7dedebc22f5181eb73573142c5 \ + --hash=sha256:fa9db3f79de01457b03d4f01b34cf91bc0048eb2c3846ff26f66687c2f6d16ab \ + --hash=sha256:fce659a462a1be54d2ffcacea5e3ba2d74daa74f30f5f143fe0c58636e355fdd \ + --hash=sha256:ffee1f21e5ef0d712f9033568f8344d5da8cc2869dbd08d87c84656e6a2d2f68 # via # jinja2 # werkzeug # wtforms -mnemonic==0.20 \ - --hash=sha256:7c6fb5639d779388027a77944680aee4870f0fcd09b1e42a5525ee2ce4c625f6 \ - --hash=sha256:acd2168872d0379e7a10873bb3e12bf6c91b35de758135c4fbd1015ef18fafc5 +mnemonic==0.21 \ + --hash=sha256:1fe496356820984f45559b1540c80ff10de448368929b9c60a2b55744cc88acf \ + --hash=sha256:72dc9de16ec5ef47287237b9b6943da11647a03fe7cf1f139fc3d7c4a7439288 # via # -r requirements.in # hwi noiseprotocol==0.3.1 \ --hash=sha256:2e1a603a38439636cf0ffd8b3e8b12cee27d368a28b41be7dbe568b2abb23111 \ --hash=sha256:b092a871b60f6a8f07f17950dc9f7098c8fe7d715b049bd4c24ee3752b90d645 - # via bitbox02 -numpy==1.24.2 \ - --hash=sha256:003a9f530e880cb2cd177cba1af7220b9aa42def9c4afc2a2fc3ee6be7eb2b22 \ - --hash=sha256:150947adbdfeceec4e5926d956a06865c1c690f2fd902efede4ca6fe2e657c3f \ - --hash=sha256:2620e8592136e073bd12ee4536149380695fbe9ebeae845b81237f986479ffc9 \ - --hash=sha256:2eabd64ddb96a1239791da78fa5f4e1693ae2dadc82a76bc76a14cbb2b966e96 \ - --hash=sha256:4173bde9fa2a005c2c6e2ea8ac1618e2ed2c1c6ec8a7657237854d42094123a0 \ - --hash=sha256:4199e7cfc307a778f72d293372736223e39ec9ac096ff0a2e64853b866a8e18a \ - --hash=sha256:4cecaed30dc14123020f77b03601559fff3e6cd0c048f8b5289f4eeabb0eb281 \ - --hash=sha256:557d42778a6869c2162deb40ad82612645e21d79e11c1dc62c6e82a2220ffb04 \ - --hash=sha256:63e45511ee4d9d976637d11e6c9864eae50e12dc9598f531c035265991910468 \ - --hash=sha256:6524630f71631be2dabe0c541e7675db82651eb998496bbe16bc4f77f0772253 \ - --hash=sha256:76807b4063f0002c8532cfeac47a3068a69561e9c8715efdad3c642eb27c0756 \ - --hash=sha256:7de8fdde0003f4294655aa5d5f0a89c26b9f22c0a58790c38fae1ed392d44a5a \ - --hash=sha256:889b2cc88b837d86eda1b17008ebeb679d82875022200c6e8e4ce6cf549b7acb \ - --hash=sha256:92011118955724465fb6853def593cf397b4a1367495e0b59a7e69d40c4eb71d \ - --hash=sha256:97cf27e51fa078078c649a51d7ade3c92d9e709ba2bfb97493007103c741f1d0 \ - --hash=sha256:9a23f8440561a633204a67fb44617ce2a299beecf3295f0d13c495518908e910 \ - --hash=sha256:a51725a815a6188c662fb66fb32077709a9ca38053f0274640293a14fdd22978 \ - --hash=sha256:a77d3e1163a7770164404607b7ba3967fb49b24782a6ef85d9b5f54126cc39e5 \ - --hash=sha256:adbdce121896fd3a17a77ab0b0b5eedf05a9834a18699db6829a64e1dfccca7f \ - --hash=sha256:c29e6bd0ec49a44d7690ecb623a8eac5ab8a923bce0bea6293953992edf3a76a \ - --hash=sha256:c72a6b2f4af1adfe193f7beb91ddf708ff867a3f977ef2ec53c0ffb8283ab9f5 \ - --hash=sha256:d0a2db9d20117bf523dde15858398e7c0858aadca7c0f088ac0d6edd360e9ad2 \ - --hash=sha256:e3ab5d32784e843fc0dd3ab6dcafc67ef806e6b6828dc6af2f689be0eb4d781d \ - --hash=sha256:e428c4fbfa085f947b536706a2fc349245d7baa8334f0c5723c56a10595f9b95 \ - --hash=sha256:e8d2859428712785e8a8b7d2b3ef0a1d1565892367b32f915c4a4df44d0e64f5 \ - --hash=sha256:eef70b4fc1e872ebddc38cddacc87c19a3709c0e3e5d20bf3954c147b1dd941d \ - --hash=sha256:f64bb98ac59b3ea3bf74b02f13836eb2e24e48e0ab0145bbda646295769bd780 \ - --hash=sha256:f9006288bcf4895917d02583cf3411f98631275bc67cce355a7f39f8c14338fa + # via hwi +numpy==1.26.4 \ + --hash=sha256:03a8c78d01d9781b28a6989f6fa1bb2c4f2d51201cf99d3dd875df6fbd96b23b \ + --hash=sha256:08beddf13648eb95f8d867350f6a018a4be2e5ad54c8d8caed89ebca558b2818 \ + --hash=sha256:1af303d6b2210eb850fcf03064d364652b7120803a0b872f5211f5234b399f20 \ + --hash=sha256:1dda2e7b4ec9dd512f84935c5f126c8bd8b9f2fc001e9f54af255e8c5f16b0e0 \ + --hash=sha256:2a02aba9ed12e4ac4eb3ea9421c420301a0c6460d9830d74a9df87efa4912010 \ + --hash=sha256:2e4ee3380d6de9c9ec04745830fd9e2eccb3e6cf790d39d7b98ffd19b0dd754a \ + --hash=sha256:3373d5d70a5fe74a2c1bb6d2cfd9609ecf686d47a2d7b1d37a8f3b6bf6003aea \ + --hash=sha256:47711010ad8555514b434df65f7d7b076bb8261df1ca9bb78f53d3b2db02e95c \ + --hash=sha256:4c66707fabe114439db9068ee468c26bbdf909cac0fb58686a42a24de1760c71 \ + --hash=sha256:50193e430acfc1346175fcbdaa28ffec49947a06918b7b92130744e81e640110 \ + --hash=sha256:52b8b60467cd7dd1e9ed082188b4e6bb35aa5cdd01777621a1658910745b90be \ + --hash=sha256:60dedbb91afcbfdc9bc0b1f3f402804070deed7392c23eb7a7f07fa857868e8a \ + --hash=sha256:62b8e4b1e28009ef2846b4c7852046736bab361f7aeadeb6a5b89ebec3c7055a \ + --hash=sha256:666dbfb6ec68962c033a450943ded891bed2d54e6755e35e5835d63f4f6931d5 \ + --hash=sha256:675d61ffbfa78604709862923189bad94014bef562cc35cf61d3a07bba02a7ed \ + --hash=sha256:679b0076f67ecc0138fd2ede3a8fd196dddc2ad3254069bcb9faf9a79b1cebcd \ + --hash=sha256:7349ab0fa0c429c82442a27a9673fc802ffdb7c7775fad780226cb234965e53c \ + --hash=sha256:7ab55401287bfec946ced39700c053796e7cc0e3acbef09993a9ad2adba6ca6e \ + --hash=sha256:7e50d0a0cc3189f9cb0aeb3a6a6af18c16f59f004b866cd2be1c14b36134a4a0 \ + --hash=sha256:95a7476c59002f2f6c590b9b7b998306fba6a5aa646b1e22ddfeaf8f78c3a29c \ + --hash=sha256:96ff0b2ad353d8f990b63294c8986f1ec3cb19d749234014f4e7eb0112ceba5a \ + --hash=sha256:9fad7dcb1aac3c7f0584a5a8133e3a43eeb2fe127f47e3632d43d677c66c102b \ + --hash=sha256:9ff0f4f29c51e2803569d7a51c2304de5554655a60c5d776e35b4a41413830d0 \ + --hash=sha256:a354325ee03388678242a4d7ebcd08b5c727033fcff3b2f536aea978e15ee9e6 \ + --hash=sha256:a4abb4f9001ad2858e7ac189089c42178fcce737e4169dc61321660f1a96c7d2 \ + --hash=sha256:ab47dbe5cc8210f55aa58e4805fe224dac469cde56b9f731a4c098b91917159a \ + --hash=sha256:afedb719a9dcfc7eaf2287b839d8198e06dcd4cb5d276a3df279231138e83d30 \ + --hash=sha256:b3ce300f3644fb06443ee2222c2201dd3a89ea6040541412b8fa189341847218 \ + --hash=sha256:b97fe8060236edf3662adfc2c633f56a08ae30560c56310562cb4f95500022d5 \ + --hash=sha256:bfe25acf8b437eb2a8b2d49d443800a5f18508cd811fea3181723922a8a82b07 \ + --hash=sha256:cd25bcecc4974d09257ffcd1f098ee778f7834c3ad767fe5db785be9a4aa9cb2 \ + --hash=sha256:d209d8969599b27ad20994c8e41936ee0964e6da07478d6c35016bc386b66ad4 \ + --hash=sha256:d5241e0a80d808d70546c697135da2c613f30e28251ff8307eb72ba696945764 \ + --hash=sha256:edd8b5fe47dab091176d21bb6de568acdd906d1887a4584a15a9a96a1dca06ef \ + --hash=sha256:f870204a840a60da0b12273ef34f7051e98c3b5961b61b0c2c1be6dfd64fbcd3 \ + --hash=sha256:ffa75af20b44f8dba823498024771d5ac50620e6915abac414251bd971b4529f # via pandas -pandas==1.5.3 \ - --hash=sha256:14e45300521902689a81f3f41386dc86f19b8ba8dd5ac5a3c7010ef8d2932813 \ - --hash=sha256:26d9c71772c7afb9d5046e6e9cf42d83dd147b5cf5bcb9d97252077118543792 \ - --hash=sha256:3749077d86e3a2f0ed51367f30bf5b82e131cc0f14260c4d3e499186fccc4406 \ - --hash=sha256:41179ce559943d83a9b4bbacb736b04c928b095b5f25dd2b7389eda08f46f373 \ - --hash=sha256:478ff646ca42b20376e4ed3fa2e8d7341e8a63105586efe54fa2508ee087f328 \ - --hash=sha256:50869a35cbb0f2e0cd5ec04b191e7b12ed688874bd05dd777c19b28cbea90996 \ - --hash=sha256:565fa34a5434d38e9d250af3c12ff931abaf88050551d9fbcdfafca50d62babf \ - --hash=sha256:5f2b952406a1588ad4cad5b3f55f520e82e902388a6d5a4a91baa8d38d23c7f6 \ - --hash=sha256:5fbcb19d6fceb9e946b3e23258757c7b225ba450990d9ed63ccceeb8cae609f7 \ - --hash=sha256:6973549c01ca91ec96199e940495219c887ea815b2083722821f1d7abfa2b4dc \ - --hash=sha256:74a3fd7e5a7ec052f183273dc7b0acd3a863edf7520f5d3a1765c04ffdb3b0b1 \ - --hash=sha256:7a0a56cef15fd1586726dace5616db75ebcfec9179a3a55e78f72c5639fa2a23 \ - --hash=sha256:7cec0bee9f294e5de5bbfc14d0573f65526071029d036b753ee6507d2a21480a \ - --hash=sha256:87bd9c03da1ac870a6d2c8902a0e1fd4267ca00f13bc494c9e5a9020920e1d51 \ - --hash=sha256:972d8a45395f2a2d26733eb8d0f629b2f90bebe8e8eddbb8829b180c09639572 \ - --hash=sha256:9842b6f4b8479e41968eced654487258ed81df7d1c9b7b870ceea24ed9459b31 \ - --hash=sha256:9f69c4029613de47816b1bb30ff5ac778686688751a5e9c99ad8c7031f6508e5 \ - --hash=sha256:a50d9a4336a9621cab7b8eb3fb11adb82de58f9b91d84c2cd526576b881a0c5a \ - --hash=sha256:bc4c368f42b551bf72fac35c5128963a171b40dce866fb066540eeaf46faa003 \ - --hash=sha256:c39a8da13cede5adcd3be1182883aea1c925476f4e84b2807a46e2775306305d \ - --hash=sha256:c3ac844a0fe00bfaeb2c9b51ab1424e5c8744f89860b138434a363b1f620f354 \ - --hash=sha256:c4c00e0b0597c8e4f59e8d461f797e5d70b4d025880516a8261b2817c47759ee \ - --hash=sha256:c74a62747864ed568f5a82a49a23a8d7fe171d0c69038b38cedf0976831296fa \ - --hash=sha256:dd05f7783b3274aa206a1af06f0ceed3f9b412cf665b7247eacd83be41cf7bf0 \ - --hash=sha256:dfd681c5dc216037e0b0a2c821f5ed99ba9f03ebcf119c7dac0e9a7b960b9ec9 \ - --hash=sha256:e474390e60ed609cec869b0da796ad94f420bb057d86784191eefc62b65819ae \ - --hash=sha256:f76d097d12c82a535fda9dfe5e8dd4127952b45fea9b0276cb30cca5ea313fbc +pandas==2.2.2 \ + --hash=sha256:001910ad31abc7bf06f49dcc903755d2f7f3a9186c0c040b827e522e9cef0863 \ + --hash=sha256:0ca6377b8fca51815f382bd0b697a0814c8bda55115678cbc94c30aacbb6eff2 \ + --hash=sha256:0cace394b6ea70c01ca1595f839cf193df35d1575986e484ad35c4aeae7266c1 \ + --hash=sha256:1cb51fe389360f3b5a4d57dbd2848a5f033350336ca3b340d1c53a1fad33bcad \ + --hash=sha256:2925720037f06e89af896c70bca73459d7e6a4be96f9de79e2d440bd499fe0db \ + --hash=sha256:3e374f59e440d4ab45ca2fffde54b81ac3834cf5ae2cdfa69c90bc03bde04d76 \ + --hash=sha256:40ae1dffb3967a52203105a077415a86044a2bea011b5f321c6aa64b379a3f51 \ + --hash=sha256:43498c0bdb43d55cb162cdc8c06fac328ccb5d2eabe3cadeb3529ae6f0517c32 \ + --hash=sha256:4abfe0be0d7221be4f12552995e58723c7422c80a659da13ca382697de830c08 \ + --hash=sha256:58b84b91b0b9f4bafac2a0ac55002280c094dfc6402402332c0913a59654ab2b \ + --hash=sha256:640cef9aa381b60e296db324337a554aeeb883ead99dc8f6c18e81a93942f5f4 \ + --hash=sha256:66b479b0bd07204e37583c191535505410daa8df638fd8e75ae1b383851fe921 \ + --hash=sha256:696039430f7a562b74fa45f540aca068ea85fa34c244d0deee539cb6d70aa288 \ + --hash=sha256:6d2123dc9ad6a814bcdea0f099885276b31b24f7edf40f6cdbc0912672e22eee \ + --hash=sha256:8635c16bf3d99040fdf3ca3db669a7250ddf49c55dc4aa8fe0ae0fa8d6dcc1f0 \ + --hash=sha256:873d13d177501a28b2756375d59816c365e42ed8417b41665f346289adc68d24 \ + --hash=sha256:8e5a0b00e1e56a842f922e7fae8ae4077aee4af0acb5ae3622bd4b4c30aedf99 \ + --hash=sha256:8e90497254aacacbc4ea6ae5e7a8cd75629d6ad2b30025a4a8b09aa4faf55151 \ + --hash=sha256:9057e6aa78a584bc93a13f0a9bf7e753a5e9770a30b4d758b8d5f2a62a9433cd \ + --hash=sha256:90c6fca2acf139569e74e8781709dccb6fe25940488755716d1d354d6bc58bce \ + --hash=sha256:92fd6b027924a7e178ac202cfbe25e53368db90d56872d20ffae94b96c7acc57 \ + --hash=sha256:9dfde2a0ddef507a631dc9dc4af6a9489d5e2e740e226ad426a05cabfbd7c8ef \ + --hash=sha256:9e79019aba43cb4fda9e4d983f8e88ca0373adbb697ae9c6c43093218de28b54 \ + --hash=sha256:a77e9d1c386196879aa5eb712e77461aaee433e54c68cf253053a73b7e49c33a \ + --hash=sha256:c7adfc142dac335d8c1e0dcbd37eb8617eac386596eb9e1a1b77791cf2498238 \ + --hash=sha256:d187d355ecec3629624fccb01d104da7d7f391db0311145817525281e2804d23 \ + --hash=sha256:ddf818e4e6c7c6f4f7c8a12709696d193976b591cc7dc50588d3d1a6b5dc8772 \ + --hash=sha256:e9b79011ff7a0f4b1d6da6a61aa1aa604fb312d6647de5bad20013682d1429ce \ + --hash=sha256:eee3a87076c0756de40b05c5e9a6069c035ba43e8dd71c379e68cab2c20f16ad # via specterext-stacktrack pgpy==0.6.0 \ --hash=sha256:279c2e353f4c3a319f00bd9bd582456e420f8a3ac6de2b4e9731444746828383 @@ -484,32 +551,23 @@ plotly==5.10.0 \ --hash=sha256:4d36d9859b7a153b273562deeed8c292587a472eb1fd57cd4158ec89d9defadb \ --hash=sha256:989b13825cc974390aa0169479485d9257d37848a47bc63957251f8e1a7046ba # via specterext-stacktrack -protobuf==3.20.2 \ - --hash=sha256:03d76b7bd42ac4a6e109742a4edf81ffe26ffd87c5993126d894fe48a120396a \ - --hash=sha256:09e25909c4297d71d97612f04f41cea8fa8510096864f2835ad2f3b3df5a5559 \ - --hash=sha256:18e34a10ae10d458b027d7638a599c964b030c1739ebd035a1dfc0e22baa3bfe \ - --hash=sha256:291fb4307094bf5ccc29f424b42268640e00d5240bf0d9b86bf3079f7576474d \ - --hash=sha256:2c0b040d0b5d5d207936ca2d02f00f765906622c07d3fa19c23a16a8ca71873f \ - --hash=sha256:384164994727f274cc34b8abd41a9e7e0562801361ee77437099ff6dfedd024b \ - --hash=sha256:3cb608e5a0eb61b8e00fe641d9f0282cd0eedb603be372f91f163cbfbca0ded0 \ - --hash=sha256:5d9402bf27d11e37801d1743eada54372f986a372ec9679673bfcc5c60441151 \ - --hash=sha256:712dca319eee507a1e7df3591e639a2b112a2f4a62d40fe7832a16fd19151750 \ - --hash=sha256:7a5037af4e76c975b88c3becdf53922b5ffa3f2cddf657574a4920a3b33b80f3 \ - --hash=sha256:8228e56a865c27163d5d1d1771d94b98194aa6917bcfb6ce139cbfa8e3c27334 \ - --hash=sha256:84a1544252a933ef07bb0b5ef13afe7c36232a774affa673fc3636f7cee1db6c \ - --hash=sha256:84fe5953b18a383fd4495d375fe16e1e55e0a3afe7b4f7b4d01a3a0649fcda9d \ - --hash=sha256:9c673c8bfdf52f903081816b9e0e612186684f4eb4c17eeb729133022d6032e3 \ - --hash=sha256:a9e5ae5a8e8985c67e8944c23035a0dff2c26b0f5070b2f55b217a1c33bbe8b1 \ - --hash=sha256:b4fdb29c5a7406e3f7ef176b2a7079baa68b5b854f364c21abe327bbeec01cdb \ - --hash=sha256:c184485e0dfba4dfd451c3bd348c2e685d6523543a0f91b9fd4ae90eb09e8422 \ - --hash=sha256:c9cdf251c582c16fd6a9f5e95836c90828d51b0069ad22f463761d27c6c19019 \ - --hash=sha256:e39cf61bb8582bda88cdfebc0db163b774e7e03364bbf9ce1ead13863e81e359 \ - --hash=sha256:e8fbc522303e09036c752a0afcc5c0603e917222d8bedc02813fd73b4b4ed804 \ - --hash=sha256:f34464ab1207114e73bba0794d1257c150a2b89b7a9faf504e00af7c9fd58978 \ - --hash=sha256:f52dabc96ca99ebd2169dadbe018824ebda08a795c7684a0b7d203a290f3adb0 +protobuf==4.23.3 \ + --hash=sha256:0149053336a466e3e0b040e54d0b615fc71de86da66791c592cc3c8d18150bf8 \ + --hash=sha256:08fe19d267608d438aa37019236db02b306e33f6b9902c3163838b8e75970223 \ + --hash=sha256:29660574cd769f2324a57fb78127cda59327eb6664381ecfe1c69731b83e8288 \ + --hash=sha256:2991f5e7690dab569f8f81702e6700e7364cc3b5e572725098215d3da5ccc6ac \ + --hash=sha256:3b01a5274ac920feb75d0b372d901524f7e3ad39c63b1a2d55043f3887afe0c1 \ + --hash=sha256:3bcbeb2bf4bb61fe960dd6e005801a23a43578200ea8ceb726d1f6bd0e562ba1 \ + --hash=sha256:447b9786ac8e50ae72cae7a2eec5c5df6a9dbf9aa6f908f1b8bda6032644ea62 \ + --hash=sha256:514b6bbd54a41ca50c86dd5ad6488afe9505901b3557c5e0f7823a0cf67106fb \ + --hash=sha256:5cb9e41188737f321f4fce9a4337bf40a5414b8d03227e1d9fbc59bc3a216e35 \ + --hash=sha256:7a92beb30600332a52cdadbedb40d33fd7c8a0d7f549c440347bc606fb3fe34b \ + --hash=sha256:84ea0bd90c2fdd70ddd9f3d3fc0197cc24ecec1345856c2b5ba70e4d99815359 \ + --hash=sha256:aca6e86a08c5c5962f55eac9b5bd6fce6ed98645d77e8bfc2b952ecd4a8e4f6a \ + --hash=sha256:cc14358a8742c4e06b1bfe4be1afbdf5c9f6bd094dff3e14edb78a1513893ff5 # via # -r requirements.in - # bitbox02 + # hwi psutil==5.9.0 \ --hash=sha256:072664401ae6e7c1bfb878c65d7282d4b4391f1bc9a56d5e03b5a490403271b5 \ --hash=sha256:1070a9b287846a21a5d572d6dddd369517510b68710fca56b0e9e02fd24bed9a \ @@ -622,21 +680,21 @@ psycopg2-binary==2.9.5 \ pyaes==1.6.1 \ --hash=sha256:02c1b1405c38d3c370b085fb952dd8bea3fadcee6411ad99f312cc129c536d8f # via hwi -pyasn1==0.4.8 \ - --hash=sha256:39c7e2ec30515947ff4e87fb6f456dfc6e84857d34be479c9d4a4ba4bf46aa5d \ - --hash=sha256:aef77c9fb94a3ac588e87841208bdec464471d9871bd5050a287cc9a475cd0ba +pyasn1==0.6.0 \ + --hash=sha256:3a35ab2c4b5ef98e17dfdec8ab074046fbda76e281c5a706ccd82328cfc8f64c \ + --hash=sha256:cca4bb0f2df5504f02f6f8a775b6e416ff9b0b3b16f7ee80b5a3153d9b804473 # via pgpy -pycparser==2.21 \ - --hash=sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9 \ - --hash=sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206 +pycparser==2.22 \ + --hash=sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6 \ + --hash=sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc # via cffi pyjwt==2.4.0 \ --hash=sha256:72d1d253f32dbd4f5c88eaf1fdc62f3a19f676ccbadb9dbc5d07e951b2b26daf \ --hash=sha256:d42908208c699b3b973cbeb01a969ba6a96c821eefb1c5bfe4c390c01d67abba # via -r requirements.in -pyopenssl==23.0.0 \ - --hash=sha256:c1cc5f86bcacefc84dada7d31175cae1b1518d5f60d3d0bb595a67822a868a6f \ - --hash=sha256:df5fc28af899e74e19fccb5510df423581047e10ab6f1f4ba1763ff5fde844c0 +pyopenssl==24.1.0 \ + --hash=sha256:17ed5be5936449c5418d1cd269a1a9e9081bc54c17aed272b45856a3d3dc86ad \ + --hash=sha256:cabed4bfaa5df9f1a16c0ef64a0cb65318b5cd077a7eda7d6970131ca2f41a6f # via -r requirements.in pyserial==3.5 \ --hash=sha256:3c77e014170dfffbd816e6ffc205e9842efb10be9f58ec16d3e8675b4925cddb \ @@ -649,9 +707,9 @@ pysocks==1.7.1 \ # via # -r requirements.in # cryptoadvance-spectrum -python-dateutil==2.8.2 \ - --hash=sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86 \ - --hash=sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9 +python-dateutil==2.9.0.post0 \ + --hash=sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3 \ + --hash=sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427 # via # flask-apscheduler # pandas @@ -663,28 +721,24 @@ pytimeparse==1.1.8 \ --hash=sha256:04b7be6cc8bd9f5647a6325444926c3ac34ee6bc7e69da4367ba282f076036bd \ --hash=sha256:e86136477be924d7e670646a98561957e8ca7308d44841e21f5ddea757556a0a # via -r requirements.in -pytz==2022.7.1 \ - --hash=sha256:01a0681c4b9684a28304615eba55d1ab31ae00bf68ec157ec3708a8182dbbcd0 \ - --hash=sha256:78f4f37d8198e0627c5f1143240bb0206b8691d8d7ac6d78fee88b78733f8c4a +pytz==2024.1 \ + --hash=sha256:2a29735ea9c18baf14b448846bde5a48030ed267578472d8955cd0e7443a9812 \ + --hash=sha256:328171f4e3623139da4983451950b28e95ac706e13f3f2630a879749e7a8b319 # via # apscheduler # flask-babel # flask-restful # pandas -pytz-deprecation-shim==0.1.0.post0 \ - --hash=sha256:8314c9692a636c8eb3bda879b9f119e350e93223ae83e70e80c31675a0fdc1a6 \ - --hash=sha256:af097bae1b616dde5c5744441e2ddc69e74dfdcb0c263129610d85b87445a59d - # via tzlocal requests==2.26.0 \ --hash=sha256:6c1246513ecd5ecd4528a0906f910e8f0f9c6b8ec72030dc9fd154dc1a6efd24 \ --hash=sha256:b8aa58f8cf793ffd8782d3d8cb19e66ef36f7aba4353eec859e74678b01b07a7 # via # -r requirements.in # cryptoadvance-spectrum -semver==2.13.0 \ - --hash=sha256:ced8b23dceb22134307c1b8abfa523da14198793d9787ac838e70e29e77458d4 \ - --hash=sha256:fa0fe2722ee1c3f57eac478820c3a5ae2f624af8264cbdf9000c980ff7f75e3f - # via bitbox02 +semver==3.0.2 \ + --hash=sha256:6253adb39c70f6e51afed2fa7152bcd414c411286088fb4b9effb133885ab4cc \ + --hash=sha256:b1ea4686fe70b981f85359eda33199d60c53964284e0cfb4977d243e37cf4bf4 + # via hwi simple-websocket==0.8.1 \ --hash=sha256:0abe874f6a0c6ddd197dbd4f0ce5708b47610a0d14fa2b0762e515659573d44a \ --hash=sha256:bab2f34151d8b9abb1ea6d911e5edc8232fc203b6f814d179db0ba3f93e4e026 @@ -709,80 +763,82 @@ specterext-stacktrack==0.3.0 \ --hash=sha256:14f96f1f552f57ba017b8bc642f07343edbb1abafe09e03bbaae179d78d7ce23 \ --hash=sha256:9e2946185730aab377951e83a27d8791a34e0f031e44f15991212b6b85722ca0 # via -r requirements.in -sqlalchemy==1.4.46 \ - --hash=sha256:07e48cbcdda6b8bc7a59d6728bd3f5f574ffe03f2c9fb384239f3789c2d95c2e \ - --hash=sha256:18cafdb27834fa03569d29f571df7115812a0e59fd6a3a03ccb0d33678ec8420 \ - --hash=sha256:1b1e5e96e2789d89f023d080bee432e2fef64d95857969e70d3cadec80bd26f0 \ - --hash=sha256:315676344e3558f1f80d02535f410e80ea4e8fddba31ec78fe390eff5fb8f466 \ - --hash=sha256:31de1e2c45e67a5ec1ecca6ec26aefc299dd5151e355eb5199cd9516b57340be \ - --hash=sha256:3d94682732d1a0def5672471ba42a29ff5e21bb0aae0afa00bb10796fc1e28dd \ - --hash=sha256:3ec187acf85984263299a3f15c34a6c0671f83565d86d10f43ace49881a82718 \ - --hash=sha256:4847f4b1d822754e35707db913396a29d874ee77b9c3c3ef3f04d5a9a6209618 \ - --hash=sha256:4d112b0f3c1bc5ff70554a97344625ef621c1bfe02a73c5d97cac91f8cd7a41e \ - --hash=sha256:51e1ba2884c6a2b8e19109dc08c71c49530006c1084156ecadfaadf5f9b8b053 \ - --hash=sha256:535377e9b10aff5a045e3d9ada8a62d02058b422c0504ebdcf07930599890eb0 \ - --hash=sha256:5dbf17ac9a61e7a3f1c7ca47237aac93cabd7f08ad92ac5b96d6f8dea4287fc1 \ - --hash=sha256:5f752676fc126edc1c4af0ec2e4d2adca48ddfae5de46bb40adbd3f903eb2120 \ - --hash=sha256:64cb0ad8a190bc22d2112001cfecdec45baffdf41871de777239da6a28ed74b6 \ - --hash=sha256:6913b8247d8a292ef8315162a51931e2b40ce91681f1b6f18f697045200c4a30 \ - --hash=sha256:69fac0a7054d86b997af12dc23f581cf0b25fb1c7d1fed43257dee3af32d3d6d \ - --hash=sha256:7001f16a9a8e06488c3c7154827c48455d1c1507d7228d43e781afbc8ceccf6d \ - --hash=sha256:7b81b1030c42b003fc10ddd17825571603117f848814a344d305262d370e7c34 \ - --hash=sha256:7f8267682eb41a0584cf66d8a697fef64b53281d01c93a503e1344197f2e01fe \ - --hash=sha256:887865924c3d6e9a473dc82b70977395301533b3030d0f020c38fd9eba5419f2 \ - --hash=sha256:9167d4227b56591a4cc5524f1b79ccd7ea994f36e4c648ab42ca995d28ebbb96 \ - --hash=sha256:939f9a018d2ad04036746e15d119c0428b1e557470361aa798e6e7d7f5875be0 \ - --hash=sha256:955162ad1a931fe416eded6bb144ba891ccbf9b2e49dc7ded39274dd9c5affc5 \ - --hash=sha256:984ee13543a346324319a1fb72b698e521506f6f22dc37d7752a329e9cd00a32 \ - --hash=sha256:9883f5fae4fd8e3f875adc2add69f8b945625811689a6c65866a35ee9c0aea23 \ - --hash=sha256:a1ad90c97029cc3ab4ffd57443a20fac21d2ec3c89532b084b073b3feb5abff3 \ - --hash=sha256:a3714e5b33226131ac0da60d18995a102a17dddd42368b7bdd206737297823ad \ - --hash=sha256:ae067ab639fa499f67ded52f5bc8e084f045d10b5ac7bb928ae4ca2b6c0429a5 \ - --hash=sha256:b33ffbdbbf5446cf36cd4cc530c9d9905d3c2fe56ed09e25c22c850cdb9fac92 \ - --hash=sha256:b6e4cb5c63f705c9d546a054c60d326cbde7421421e2d2565ce3e2eee4e1a01f \ - --hash=sha256:b7f4b6aa6e87991ec7ce0e769689a977776db6704947e562102431474799a857 \ - --hash=sha256:c04144a24103135ea0315d459431ac196fe96f55d3213bfd6d39d0247775c854 \ - --hash=sha256:c522e496f9b9b70296a7675272ec21937ccfc15da664b74b9f58d98a641ce1b6 \ - --hash=sha256:c5a99282848b6cae0056b85da17392a26b2d39178394fc25700bcf967e06e97a \ - --hash=sha256:c7a46639ba058d320c9f53a81db38119a74b8a7a1884df44d09fbe807d028aaf \ - --hash=sha256:d4b1cc7835b39835c75cf7c20c926b42e97d074147c902a9ebb7cf2c840dc4e2 \ - --hash=sha256:d4d164df3d83d204c69f840da30b292ac7dc54285096c6171245b8d7807185aa \ - --hash=sha256:d61e9ecc849d8d44d7f80894ecff4abe347136e9d926560b818f6243409f3c86 \ - --hash=sha256:d68e1762997bfebf9e5cf2a9fd0bcf9ca2fdd8136ce7b24bbd3bbfa4328f3e4a \ - --hash=sha256:e3c1808008124850115a3f7e793a975cfa5c8a26ceeeb9ff9cbb4485cac556df \ - --hash=sha256:f8cb80fe8d14307e4124f6fad64dfd87ab749c9d275f82b8b4ec84c84ecebdbe +sqlalchemy==1.4.52 \ + --hash=sha256:1296f2cdd6db09b98ceb3c93025f0da4835303b8ac46c15c2136e27ee4d18d94 \ + --hash=sha256:1e135fff2e84103bc15c07edd8569612ce317d64bdb391f49ce57124a73f45c5 \ + --hash=sha256:1f8e1c6a6b7f8e9407ad9afc0ea41c1f65225ce505b79bc0342159de9c890782 \ + --hash=sha256:24bb0f81fbbb13d737b7f76d1821ec0b117ce8cbb8ee5e8641ad2de41aa916d3 \ + --hash=sha256:29d4247313abb2015f8979137fe65f4eaceead5247d39603cc4b4a610936cd2b \ + --hash=sha256:2c286fab42e49db23c46ab02479f328b8bdb837d3e281cae546cc4085c83b680 \ + --hash=sha256:2f251af4c75a675ea42766880ff430ac33291c8d0057acca79710f9e5a77383d \ + --hash=sha256:346ed50cb2c30f5d7a03d888e25744154ceac6f0e6e1ab3bc7b5b77138d37710 \ + --hash=sha256:3491c85df263a5c2157c594f54a1a9c72265b75d3777e61ee13c556d9e43ffc9 \ + --hash=sha256:427988398d2902de042093d17f2b9619a5ebc605bf6372f7d70e29bde6736842 \ + --hash=sha256:427c282dd0deba1f07bcbf499cbcc9fe9a626743f5d4989bfdfd3ed3513003dd \ + --hash=sha256:49e3772eb3380ac88d35495843daf3c03f094b713e66c7d017e322144a5c6b7c \ + --hash=sha256:4dae6001457d4497736e3bc422165f107ecdd70b0d651fab7f731276e8b9e12d \ + --hash=sha256:5b5de6af8852500d01398f5047d62ca3431d1e29a331d0b56c3e14cb03f8094c \ + --hash=sha256:5bbce5dd7c7735e01d24f5a60177f3e589078f83c8a29e124a6521b76d825b85 \ + --hash=sha256:5bed4f8c3b69779de9d99eb03fd9ab67a850d74ab0243d1be9d4080e77b6af12 \ + --hash=sha256:618827c1a1c243d2540314c6e100aee7af09a709bd005bae971686fab6723554 \ + --hash=sha256:6ab773f9ad848118df7a9bbabca53e3f1002387cdbb6ee81693db808b82aaab0 \ + --hash=sha256:6e41cb5cda641f3754568d2ed8962f772a7f2b59403b95c60c89f3e0bd25f15e \ + --hash=sha256:7027be7930a90d18a386b25ee8af30514c61f3852c7268899f23fdfbd3107181 \ + --hash=sha256:763bd97c4ebc74136ecf3526b34808c58945023a59927b416acebcd68d1fc126 \ + --hash=sha256:7d0dbc56cb6af5088f3658982d3d8c1d6a82691f31f7b0da682c7b98fa914e91 \ + --hash=sha256:80e63bbdc5217dad3485059bdf6f65a7d43f33c8bde619df5c220edf03d87296 \ + --hash=sha256:80e7f697bccc56ac6eac9e2df5c98b47de57e7006d2e46e1a3c17c546254f6ef \ + --hash=sha256:84e10772cfc333eb08d0b7ef808cd76e4a9a30a725fb62a0495877a57ee41d81 \ + --hash=sha256:853fcfd1f54224ea7aabcf34b227d2b64a08cbac116ecf376907968b29b8e763 \ + --hash=sha256:99224d621affbb3c1a4f72b631f8393045f4ce647dd3262f12fe3576918f8bf3 \ + --hash=sha256:a251146b921725547ea1735b060a11e1be705017b568c9f8067ca61e6ef85f20 \ + --hash=sha256:a551d5f3dc63f096ed41775ceec72fdf91462bb95abdc179010dc95a93957800 \ + --hash=sha256:a5d2e08d79f5bf250afb4a61426b41026e448da446b55e4770c2afdc1e200fce \ + --hash=sha256:a752bff4796bf22803d052d4841ebc3c55c26fb65551f2c96e90ac7c62be763a \ + --hash=sha256:afb1672b57f58c0318ad2cff80b384e816735ffc7e848d8aa51e0b0fc2f4b7bb \ + --hash=sha256:bcdfb4b47fe04967669874fb1ce782a006756fdbebe7263f6a000e1db969120e \ + --hash=sha256:bdb7b4d889631a3b2a81a3347c4c3f031812eb4adeaa3ee4e6b0d028ad1852b5 \ + --hash=sha256:c124912fd4e1bb9d1e7dc193ed482a9f812769cb1e69363ab68e01801e859821 \ + --hash=sha256:c294ae4e6bbd060dd79e2bd5bba8b6274d08ffd65b58d106394cb6abbf35cf45 \ + --hash=sha256:ca5ce82b11731492204cff8845c5e8ca1a4bd1ade85e3b8fcf86e7601bfc6a39 \ + --hash=sha256:cb8f9e4c4718f111d7b530c4e6fb4d28f9f110eb82e7961412955b3875b66de0 \ + --hash=sha256:d2de46f5d5396d5331127cfa71f837cca945f9a2b04f7cb5a01949cf676db7d1 \ + --hash=sha256:d913f8953e098ca931ad7f58797f91deed26b435ec3756478b75c608aa80d139 \ + --hash=sha256:de9acf369aaadb71a725b7e83a5ef40ca3de1cf4cdc93fa847df6b12d3cd924b \ + --hash=sha256:e93983cc0d2edae253b3f2141b0a3fb07e41c76cd79c2ad743fc27eb79c3f6db \ + --hash=sha256:f12aaf94f4d9679ca475975578739e12cc5b461172e04d66f7a3c39dd14ffc64 \ + --hash=sha256:f68016f9a5713684c1507cc37133c28035f29925c75c0df2f9d0f7571e23720a \ + --hash=sha256:f7ea11727feb2861deaa293c7971a4df57ef1c90e42cb53f0da40c3468388000 \ + --hash=sha256:f98dbb8fcc6d1c03ae8ec735d3c62110949a3b8bc6e215053aa27096857afb45 # via # cryptoadvance-spectrum # flask-sqlalchemy stem==1.8.0 \ --hash=sha256:a0b48ea6224e95f22aa34c0bc3415f0eb4667ddeae3dfb5e32a6920c185568c2 # via -r requirements.in -tenacity==8.2.1 \ - --hash=sha256:c7bb4b86425b977726a7b49971542d4f67baf72096597d283f3ffd01f33b92df \ - --hash=sha256:dd1b769ca7002fda992322939feca5bee4fa11f39146b0af14e0b8d9f27ea854 +tenacity==8.3.0 \ + --hash=sha256:3649f6443dbc0d9b01b9d8020a9c4ec7a1ff5f6f3c6c8a036ef371f573fe9185 \ + --hash=sha256:953d4e6ad24357bceffbc9707bc74349aca9d245f68eb65419cf0c249a1949a2 # via plotly -typing-extensions==3.10.0.2 \ - --hash=sha256:49f75d16ff11f1cd258e1b988ccff82a3ca5570217d7ad8c5f48205dd99a677e \ - --hash=sha256:d8226d10bc02a29bcc81df19a26e56a9647f8b0a6d4a83924139f4a8b01f17b7 \ - --hash=sha256:f1d25edafde516b146ecd0613dabcc61409817af4766fbbcfb8d1ad4ec441a34 - # via - # bitbox02 - # hwi -tzdata==2022.7 \ - --hash=sha256:2b88858b0e3120792a3c0635c23daf36a7d7eeeca657c323da299d2094402a0d \ - --hash=sha256:fe5f866eddd8b96e9fcba978f8e503c909b19ea7efda11e52e39494bad3a7bfa - # via pytz-deprecation-shim -tzlocal==4.2 \ - --hash=sha256:89885494684c929d9191c57aa27502afc87a579be5cdd3225c77c463ea043745 \ - --hash=sha256:ee5842fa3a795f023514ac2d801c4a81d1743bbe642e3940143326b3a00addd7 +typing-extensions==4.11.0 \ + --hash=sha256:83f085bd5ca59c80295fc2a82ab5dac679cbe02b9f33f7d83af68e241bea51b0 \ + --hash=sha256:c1f94d72897edaf4ce775bb7558d5b79d8126906a14ea5ed1635921406c0387a + # via hwi +tzdata==2024.1 \ + --hash=sha256:2674120f8d891909751c38abcdfd386ac0a5a1127954fbc332af6b5ceae07efd \ + --hash=sha256:9068bc196136463f5245e51efda838afa15aaeca9903f49050dfa2679db4d252 + # via pandas +tzlocal==5.2 \ + --hash=sha256:49816ef2fe65ea8ac19d19aa7a1ae0551c834303d5014c6d5a62e4cbda8047b8 \ + --hash=sha256:8d399205578f1a9342816409cc1e46a93ebd5755e39ea2d85334bea911bf0e6e # via apscheduler -urllib3==1.26.14 \ - --hash=sha256:076907bf8fd355cde77728471316625a4d2f7e713c125f51953bb5b3eecf4f72 \ - --hash=sha256:75edcdc2f7d85b137124a6c3c9fc3933cdeaa12ecb9a6a959f22797a0feca7e1 +urllib3==1.26.18 \ + --hash=sha256:34b97092d7e0a3a8cf7cd10e386f401b3737364026c45e622aa02903dffe0f07 \ + --hash=sha256:f8ecc1bba5667413457c529ab955bf8c67b45db799d159066261719e328580a0 # via requests -werkzeug==2.2.3 \ - --hash=sha256:2e1ccc9417d4da358b9de6f174e3ac094391ea1d4fbef2d667865d819dfd0afe \ - --hash=sha256:56433961bc1f12533306c624f3be5e744389ac61d722175d543e1751285da612 +werkzeug==3.0.3 \ + --hash=sha256:097e5bfda9f0aba8da6b8545146def481d06aa7d3266e7448e2cccf67dd8bd18 \ + --hash=sha256:fc9645dc43e03e4d630d23143a04a7f947a9a3b5727cd535fdfe155a17cc48c8 # via # flask # flask-login @@ -790,9 +846,9 @@ wsproto==1.2.0 \ --hash=sha256:ad565f26ecb92588a3e43bc3d96164de84cd9902482b130d0ddbaa9664a85065 \ --hash=sha256:b9acddd652b585d75b20477888c56642fdade28bdfd3579aa24a4d2c037dd736 # via simple-websocket -wtforms==3.0.1 \ - --hash=sha256:6b351bbb12dd58af57ffef05bc78425d08d1914e0fd68ee14143b7ade023c5bc \ - --hash=sha256:837f2f0e0ca79481b92884962b914eba4e72b7a2daaf1f939c890ed0124b834b +wtforms==3.1.2 \ + --hash=sha256:bf831c042829c8cdbad74c27575098d541d039b1faa74c771545ecac916f2c07 \ + --hash=sha256:f8d76180d7239c94c6322f7990ae1216dae3659b7aa1cee94b6318bdffb474b9 # via flask-wtf # WARNING: The following packages were not pinned, but pip requires them to be diff --git a/src/cryptoadvance/specter/cli/cli_server.py b/src/cryptoadvance/specter/cli/cli_server.py index 46032dd486..bf3d4c9c10 100644 --- a/src/cryptoadvance/specter/cli/cli_server.py +++ b/src/cryptoadvance/specter/cli/cli_server.py @@ -58,9 +58,9 @@ def cli(): help="Start the hwi-bridge to use your HWWs with a remote specter.", ) @click.option( - "--enforcehwiinitialisation", + "--skiphwiinitialisation", is_flag=True, - help="calls enumerate() which is known to cause issues with certain usb-devices plugged in at startup.", + help="Skips to call HWI's enumerate() on start-up", ) @click.option( "--devstatus-threshold", @@ -88,7 +88,7 @@ def server( filelog, tor, hwibridge, - enforcehwiinitialisation, + skiphwiinitialisation, devstatus_threshold, specter_data_folder, config, @@ -154,8 +154,8 @@ def server( kwargs = configure_ssl(kwargs, app.config, ssl) app.app_context().push() - if enforcehwiinitialisation: - app.config["ENFORCE_HWI_INITIALISATION_AT_STARTUP"] = True + if skiphwiinitialisation: + app.config["SKIP_HWI_INITIALISATION_AT_STARTUP"] = True init_app(app, hwibridge=hwibridge) if filelog: diff --git a/src/cryptoadvance/specter/config.py b/src/cryptoadvance/specter/config.py index f592a2bd26..af73dee8dc 100644 --- a/src/cryptoadvance/specter/config.py +++ b/src/cryptoadvance/specter/config.py @@ -82,9 +82,9 @@ class BaseConfig(object): CERT = os.getenv("CERT", None) KEY = os.getenv("KEY", None) - # It might be necessary to enforce the HWI initialisation - ENFORCE_HWI_INITIALISATION_AT_STARTUP = _get_bool_env_var( - "ENFORCE_HWI_INITIALISATION_AT_STARTUP", False + # It might be necessary / useful for testing to skip the HWI initialisation (i.e. calling enumerate on startup) + SKIP_HWI_INITIALISATION_AT_STARTUP = _get_bool_env_var( + "SKIP_HWI_INITIALISATION_AT_STARTUP", False ) # This will be used to search for a bitcoin.conf in order to enable the diff --git a/src/cryptoadvance/specter/devices/bitbox02.py b/src/cryptoadvance/specter/devices/bitbox02.py index b370ccf41a..a1678fb8dc 100644 --- a/src/cryptoadvance/specter/devices/bitbox02.py +++ b/src/cryptoadvance/specter/devices/bitbox02.py @@ -6,3 +6,15 @@ class BitBox02(HWIDevice): name = "BitBox02" icon = "img/devices/bitbox02_icon.svg" supports_hwi_multisig_display_address = True + + @classmethod + def get_client(cls, *args, **kwargs): + # Convert args tuple to a list to modify it + args_list = list(args) + + if args_list[1] == "": # passphrase + # prevent this error-message raised from hwilib since about 2.2.1: + # "Internal error: The BitBox02 does not accept a passphrase from the host. Please enable the passphrase option and enter the passphrase on the device during unlock." + # IMHO hwilib should treat "" same as none but it does not + args_list[1] = None + return super().get_client(*args_list, **kwargs) diff --git a/src/cryptoadvance/specter/devices/bitcoin_core.py b/src/cryptoadvance/specter/devices/bitcoin_core.py index 2a67b29fbf..1a6facff6f 100644 --- a/src/cryptoadvance/specter/devices/bitcoin_core.py +++ b/src/cryptoadvance/specter/devices/bitcoin_core.py @@ -7,7 +7,7 @@ from embit import bip32, bip39, networks from ..device import Device -from ..helpers import alias +from ..helpers import create_unique_id from ..key import Key from ..rpc import get_default_datadir from ..specter_error import SpecterError @@ -115,9 +115,11 @@ def add_hot_wallet_keys( { "desc": AddChecksum( "{}({}{}/0/*)".format( - "tr" - if path.startswith("m/86h") and taproot_available - else "wpkh", + ( + "tr" + if path.startswith("m/86h") and taproot_available + else "wpkh" + ), xprv, path.rstrip("/").replace("m", ""), ) @@ -130,9 +132,11 @@ def add_hot_wallet_keys( { "desc": AddChecksum( "{}({}{}/1/*)".format( - "tr" - if path.startswith("m/86h") and taproot_available - else "wpkh", + ( + "tr" + if path.startswith("m/86h") and taproot_available + else "wpkh" + ), xprv, path.rstrip("/").replace("m", ""), ) diff --git a/src/cryptoadvance/specter/devices/hwi/jade.py b/src/cryptoadvance/specter/devices/hwi/jade.py index b57eb2d096..b9404100ed 100644 --- a/src/cryptoadvance/specter/devices/hwi/jade.py +++ b/src/cryptoadvance/specter/devices/hwi/jade.py @@ -9,33 +9,25 @@ from functools import wraps from typing import Any, Callable, Dict, List, Optional, Sequence, Tuple, Union -from hwilib.descriptor import PubkeyProvider, MultisigDescriptor +from hwilib.descriptor import MultisigDescriptor from hwilib.hwwclient import HardwareWalletClient from hwilib.errors import ( ActionCanceledError, BadArgumentError, DeviceConnectionError, DeviceFailureError, + DeviceNotReadyError, UnavailableActionError, common_err_msgs, handle_errors, ) -from hwilib.common import ( - AddressType, - Chain, -) -from hwilib.key import ExtendedKey, parse_path, KeyOriginInfo, is_hardened +from hwilib.common import AddressType, Chain, sha256 +from hwilib.key import ExtendedKey, KeyOriginInfo, is_hardened, parse_path from hwilib.psbt import PSBT -from hwilib.tx import CTransaction -from hwilib._script import ( - is_p2sh, - is_p2wpkh, - is_p2wsh, - is_witness, - parse_multisig, -) +from hwilib._script import is_p2sh, is_p2wpkh, is_p2wsh, is_witness, parse_multisig import logging +import semver import os import hashlib @@ -48,11 +40,17 @@ from embit.util import secp256k1 from embit.liquid.finalizer import finalize_psbt from embit.liquid.transaction import write_commitment +from embit.descriptor import Descriptor # The test emulator port -SIMULATOR_PATH = "tcp:127.0.0.1:2222" - -JADE_DEVICE_IDS = [(0x10C4, 0xEA60), (0x1A86, 0x55D4)] +SIMULATOR_PATH = "tcp:127.0.0.1:30121" + +JADE_DEVICE_IDS = [ + (0x10C4, 0xEA60), + (0x1A86, 0x55D4), + (0x0403, 0x6001), + (0x1A86, 0x7523), +] HAS_NETWORKING = hasattr(jade, "_http_request") py_enumerate = ( @@ -94,26 +92,19 @@ def func(*args: Any, **kwargs: Any) -> Any: # This class extends the HardwareWalletClient for Blockstream Jade specific things class JadeClient(HardwareWalletClient): + MIN_SUPPORTED_FW_VERSION = semver.VersionInfo(0, 1, 32) NETWORKS = { Chain.MAIN: "mainnet", Chain.TEST: "testnet", + Chain.SIGNET: "testnet", # same as far as Jade is concerned Chain.REGTEST: "localtest", } - liquid_network = None - def set_liquid_network(self, chain): - if chain == "liquidv1": - self.liquid_network = "liquid" - elif chain == "liquidtestnet": - self.liquid_network = "testnet-liquid" - else: - self.liquid_network = "localtest-liquid" - - def _network(self): - if self.liquid_network: - return self.liquid_network - return JadeClient.NETWORKS.get(self.chain, "mainnet") + def _network(self) -> str: + if self.chain not in self.NETWORKS: + raise BadArgumentError(f"Unhandled network: {self.chain}") + return self.NETWORKS[self.chain] ADDRTYPES = { AddressType.LEGACY: "pkh(k)", @@ -126,12 +117,11 @@ def _network(self): AddressType.SH_WIT: "sh(wsh(multi(k)))", } - @staticmethod - def _convertAddrType(addrType, multisig=False): - if multisig: - return JadeClient.MULTI_ADDRTYPES[addrType] - return JadeClient.ADDRTYPES[addrType] + @classmethod + def _convertAddrType(cls, addrType: AddressType, multisig: bool) -> str: + return cls.MULTI_ADDRTYPES[addrType] if multisig else cls.ADDRTYPES[addrType] + # Derive a deterministic name for a multisig registration record (ignoring bip67 key sorting) @staticmethod def _get_multisig_name( type: str, threshold: int, signers: List[Tuple[bytes, Sequence[int]]] @@ -142,31 +132,58 @@ def _get_multisig_name( summary += fingerprint.hex() + "|" + str(path) + "|" # Hash it, get the first 6-bytes as hex, prepend with 'hwi' - hash_summary = hashlib.sha256(summary.encode()).digest().hex() + hash_summary = sha256(summary.encode()).hex() return "hwi" + hash_summary[:12] - def __init__(self, path: str, password: str = "", expert: bool = False) -> None: - super(JadeClient, self).__init__(path, password, expert) - self.jade = JadeAPI.create_serial(path) + def __init__( + self, + path: str, + password: Optional[str] = None, + expert: bool = False, + chain: Chain = Chain.MAIN, + skip_unlocking: bool = False, + timeout: Optional[int] = None, + ) -> None: + super(JadeClient, self).__init__(path, password, expert, chain) + self.jade = JadeAPI.create_serial(path, timeout=timeout) self.jade.connect() - # Push some host entropy into jade - self.jade.add_entropy(os.urandom(32)) + verinfo = self.jade.get_version_info() + uninitialized = verinfo["JADE_STATE"] not in ["READY", "TEMP"] - # Do the PIN thing if required - # NOTE: uses standard 'requests' networking to connect to blind pinserver - try: - while not self.jade.auth_user(self._network()): - logging.debug("Incorrect PIN provided") - except: - try: - self.chain = Chain.TEST - while not self.jade.auth_user(self._network()): - logging.debug("Incorrect PIN provided") - except: - self.chain = Chain.REGTEST - while not self.jade.auth_user(self._network()): - logging.debug("Incorrect PIN provided") + # Check minimum supported firmware version (ignore candidate/build parts) + fw_version = semver.parse_version_info(verinfo["JADE_VERSION"]) + if self.MIN_SUPPORTED_FW_VERSION > fw_version.finalize_version(): + raise DeviceNotReadyError( + f"Jade fw version: {fw_version} - minimum required version: {self.MIN_SUPPORTED_FW_VERSION}. " + "Please update using a Blockstream Green companion app" + ) + if path == SIMULATOR_PATH: + if uninitialized: + # Connected to simulator but it appears to have no wallet set + raise DeviceNotReadyError( + "Use JadeAPI.set_[seed|mnemonic] to set simulator wallet" + ) + else: + if uninitialized: + if skip_unlocking: + # We don't want to prompt to unlock the device right now + return + if not HAS_NETWORKING: + # Wallet not initialised/unlocked nor do we have networking dependencies + # User must use 'Recovery Phrase Login' or 'QR Unlock' feature to access wallet + raise DeviceNotReadyError( + 'Use "Recovery Phrase Login" or "QR PIN Unlock" feature on Jade hw to access wallet' + ) + + # Push some host entropy into jade + self.jade.add_entropy(os.urandom(32)) + + # Authenticate the user - this may require a PIN and pinserver interaction + # (if we have required networking dependencies) + authenticated = False + while not authenticated: + authenticated = self.jade.auth_user(self._network()) # Retrieves the public key at the specified BIP 32 derivation path @jade_exception @@ -176,13 +193,6 @@ def get_pubkey_at_path(self, bip32_path: str) -> ExtendedKey: ext_key = ExtendedKey.deserialize(xpub) return ext_key - @jade_exception - def get_master_blinding_key(self) -> str: - mbk = self.jade.get_master_blinding_key() - assert len(mbk) == 32 - bkey = ec.PrivateKey(mbk) - return bkey.wif() - # Walk the PSBT looking for inputs we can sign. Push any signatures into the # 'partial_sigs' map in the input, and return the updated PSBT. @jade_exception @@ -209,12 +219,9 @@ def _split_at_last_hardened_element( prefix, suffix = _split_at_last_hardened_element(origin.path) signers.append((origin.fingerprint, prefix)) paths.append(suffix) - # sort signers and paths like in multisig registration - signers, paths = [list(a) for a in zip(*sorted(zip(signers, paths)))] - return signers, paths - c_txn = CTransaction(tx.tx) + c_txn = tx.get_unsigned_tx() master_fp = self.get_master_fingerprint() signing_singlesigs = False signing_multisigs = {} @@ -226,7 +233,7 @@ def _split_at_last_hardened_element( # Signing input details jade_inputs = [] - for n_vin, (txin, psbtin) in py_enumerate(zip(c_txn.vin, tx.inputs)): + for n_vin, psbtin in py_enumerate(tx.inputs): # Get bip32 path to use to sign, if required for this input path = None multisig_input = len(psbtin.hd_keypaths) > 1 @@ -252,7 +259,12 @@ def _split_at_last_hardened_element( if psbtin.witness_utxo: utxo = psbtin.witness_utxo if psbtin.non_witness_utxo: - utxo = psbtin.non_witness_utxo.vout[txin.prevout.n] + if psbtin.prev_txid != psbtin.non_witness_utxo.hash: + raise BadArgumentError( + f"Input {n_vin} has a non_witness_utxo with the wrong hash" + ) + assert psbtin.prev_out is not None + utxo = psbtin.non_witness_utxo.vout[psbtin.prev_out] input_txn_bytes = ( psbtin.non_witness_utxo.serialize_without_witness() ) @@ -260,6 +272,7 @@ def _split_at_last_hardened_element( raise Exception( "PSBT is missing input utxo information, cannot sign" ) + sats_value = utxo.nValue scriptcode = utxo.scriptPubKey if is_p2sh(scriptcode): @@ -311,9 +324,10 @@ def _split_at_last_hardened_element( jade_inputs.append( { "is_witness": witness_input, - "input_tx": input_txn_bytes, + "satoshi": sats_value, "script": scriptcode, "path": path, + "input_tx": input_txn_bytes, "ae_host_entropy": os.urandom(32), "ae_host_commitment": os.urandom(32), } @@ -321,157 +335,123 @@ def _split_at_last_hardened_element( # Change output details # This is optional, in that if we send it Jade validates the change output script - # and the user need not confirm that ouptut. If not passed the change output must + # and the user need not confirm that output. If not passed the change output must # be confirmed by the user on the hwwallet screen, like any other spend output. change: List[Optional[Dict[str, Any]]] = [None] * len(tx.outputs) - # If signing multisig inputs, get registered multisigs details in case we - # see any multisig outputs which may be change which we can auto-validate. - # ie. filter speculative 'signing multisigs' to ones actually registered on the hw - candidate_multisigs = {} - - if signing_multisigs: - # register multisig if xpubs are known - if tx.xpub and len(signing_multisigs) == 1: - msigname = list(signing_multisigs.keys())[0] - signers = [] - origins = [] - for xpub in tx.xpub: - hd = bip32.HDKey.parse(xpub) - origin = tx.xpub[xpub] - origins.append((origin.fingerprint, origin.path)) - - signers.append( - { - "fingerprint": origin.fingerprint, - "derivation": origin.path, - "xpub": str(hd), - "path": [], - } - ) - - # sort origins and signers together - origins, signers = [ - list(a) for a in zip(*sorted(zip(origins, signers))) - ] - - # Get a deterministic name for this multisig wallet - script_variant = signing_multisigs[msigname][0] - thresh = signing_multisigs[msigname][1] - num_signers = signing_multisigs[msigname][2] - multisig_name = self._get_multisig_name( - script_variant, thresh, origins - ) - # stupid sanity check of the fingerprints and origins - if multisig_name == msigname: - # Need to ensure this multisig wallet is registered first - # (Note: 're-registering' is a no-op) - self.jade.register_multisig( - self._network(), - multisig_name, - script_variant, - True, # always use sorted - thresh, - signers, - ) - # - registered_multisigs = self.jade.get_registered_multisigs() - signing_multisigs = { - k: v - for k, v in signing_multisigs.items() - if k in registered_multisigs - and registered_multisigs[k]["variant"] == v[0] - and registered_multisigs[k]["threshold"] == v[1] - and registered_multisigs[k]["num_signers"] == len(v[2]) - } - - # Look at every output... - for n_vout, (txout, psbtout) in py_enumerate(zip(c_txn.vout, tx.outputs)): - num_signers = len(psbtout.hd_keypaths) - - if num_signers == 1 and signing_singlesigs: - # Single-sig output - since we signed singlesig inputs this could be our change - for pubkey, origin in psbtout.hd_keypaths.items(): - # Considers 'our' outputs as potential change as far as Jade is concerned - # ie. can be verified and auto-confirmed. - # Is this ok, or should check path also, assuming bip44-like ? - if origin.fingerprint == master_fp and len(origin.path) > 0: - change_addr_type = None - if txout.is_p2pkh(): - change_addr_type = AddressType.LEGACY - elif txout.is_witness()[0] and not txout.is_p2wsh(): - change_addr_type = AddressType.WIT # ie. p2wpkh - elif ( - txout.is_p2sh() and is_witness(psbtout.redeem_script)[0] - ): - change_addr_type = AddressType.SH_WIT - else: - continue - - script_variant = self._convertAddrType( - change_addr_type, multisig=False - ) - change[n_vout] = { - "path": origin.path, - "variant": script_variant, - } - - elif num_signers > 1 and signing_multisigs: - # Multisig output - since we signed multisig inputs this could be our change - candidate_multisigs = { + # Skip automatic change validation in expert mode - user checks *every* output on hw + if not self.expert: + # If signing multisig inputs, get registered multisigs details in case we + # see any multisig outputs which may be change which we can auto-validate. + # ie. filter speculative 'signing multisigs' to ones actually registered on the hw + if signing_multisigs: + registered_multisigs = self.jade.get_registered_multisigs() + signing_multisigs = { k: v for k, v in signing_multisigs.items() - if len(v[2]) == num_signers + if k in registered_multisigs + and registered_multisigs[k]["variant"] == v[0] + and registered_multisigs[k]["threshold"] == v[1] + and registered_multisigs[k]["num_signers"] == len(v[2]) } - if not candidate_multisigs: - continue - for pubkey, origin in psbtout.hd_keypaths.items(): - if origin.fingerprint == master_fp and len(origin.path) > 0: - change_addr_type = None - if ( - txout.is_p2sh() - and not is_witness(psbtout.redeem_script)[0] - ): - change_addr_type = AddressType.LEGACY - scriptcode = psbtout.redeem_script - elif txout.is_p2wsh() and not txout.is_p2sh(): - change_addr_type = AddressType.WIT - scriptcode = psbtout.witness_script - elif ( - txout.is_p2sh() and is_witness(psbtout.redeem_script)[0] - ): - change_addr_type = AddressType.SH_WIT - scriptcode = psbtout.witness_script - else: - continue + # Look at every output... + for n_vout, (txout, psbtout) in py_enumerate( + zip(c_txn.vout, tx.outputs) + ): + num_signers = len(psbtout.hd_keypaths) + + if num_signers == 1 and signing_singlesigs: + # Single-sig output - since we signed singlesig inputs this could be our change + for pubkey, origin in psbtout.hd_keypaths.items(): + # Considers 'our' outputs as potential change as far as Jade is concerned + # ie. can be verified and auto-confirmed. + # Is this ok, or should check path also, assuming bip44-like ? + if origin.fingerprint == master_fp and len(origin.path) > 0: + change_addr_type = None + if txout.is_p2pkh(): + change_addr_type = AddressType.LEGACY + elif txout.is_witness()[0] and not txout.is_p2wsh(): + change_addr_type = AddressType.WIT # ie. p2wpkh + elif ( + txout.is_p2sh() + and is_witness(psbtout.redeem_script)[0] + ): + change_addr_type = AddressType.SH_WIT + else: + continue - parsed = parse_multisig(scriptcode) - if parsed: script_variant = self._convertAddrType( - change_addr_type, multisig=True - ) - threshold = parsed[0] - - pubkeys = parsed[1] - hd_keypath_origins = [ - psbtout.hd_keypaths[pubkey] for pubkey in pubkeys - ] - - signers, paths = _parse_signers(hd_keypath_origins) - - multisig_name = self._get_multisig_name( - script_variant, threshold, signers + change_addr_type, multisig=False ) - - matched_multisig = candidate_multisigs.get( - multisig_name - ) == (script_variant, threshold, signers) - if matched_multisig: - change[n_vout] = { - "paths": paths, - "multisig_name": multisig_name, - } + change[n_vout] = { + "path": origin.path, + "variant": script_variant, + } + + elif num_signers > 1 and signing_multisigs: + # Multisig output - since we signed multisig inputs this could be our change + candidate_multisigs = { + k: v + for k, v in signing_multisigs.items() + if len(v[2]) == num_signers + } + if not candidate_multisigs: + continue + + for pubkey, origin in psbtout.hd_keypaths.items(): + if origin.fingerprint == master_fp and len(origin.path) > 0: + change_addr_type = None + if ( + txout.is_p2sh() + and not is_witness(psbtout.redeem_script)[0] + ): + change_addr_type = AddressType.LEGACY + scriptcode = psbtout.redeem_script + elif txout.is_p2wsh() and not txout.is_p2sh(): + change_addr_type = AddressType.WIT + scriptcode = psbtout.witness_script + elif ( + txout.is_p2sh() + and is_witness(psbtout.redeem_script)[0] + ): + change_addr_type = AddressType.SH_WIT + scriptcode = psbtout.witness_script + else: + continue + + parsed = parse_multisig(scriptcode) + if parsed: + script_variant = self._convertAddrType( + change_addr_type, multisig=True + ) + threshold = parsed[0] + + pubkeys = parsed[1] + hd_keypath_origins = [ + psbtout.hd_keypaths[pubkey] + for pubkey in pubkeys + ] + + signers, paths = _parse_signers(hd_keypath_origins) + multisig_name = self._get_multisig_name( + script_variant, threshold, signers + ) + matched_multisig = candidate_multisigs.get( + multisig_name + ) + + if ( + matched_multisig + and matched_multisig[0] == script_variant + and matched_multisig[1] == threshold + and sorted(matched_multisig[2]) + == sorted(signers) + ): + change[n_vout] = { + "paths": paths, + "multisig_name": multisig_name, + } # The txn itself txn_bytes = c_txn.serialize_without_witness() @@ -499,23 +479,25 @@ def sign_message(self, message: Union[str, bytes], bip32_path: str) -> str: path = parse_path(bip32_path) if isinstance(message, bytes) or isinstance(message, bytearray): message = message.decode("utf-8") + + # NOTE: tests fail if we try to use AE signatures, so stick with default (rfc6979) signature = self.jade.sign_message(path, message) - return signature + return str(signature) - # Display address of specified type on the device. Only supports single-key based addresses atm. + # Display address of specified type on the device. @jade_exception def display_singlesig_address(self, bip32_path: str, addr_type: AddressType) -> str: path = parse_path(bip32_path) - addr_type = self._convertAddrType(addr_type) + script_variant = self._convertAddrType(addr_type, multisig=False) address = self.jade.get_receive_address( - self._network(), path, variant=addr_type + self._network(), path, variant=script_variant ) - return address + return str(address) + # Display multisig address of specified type on the device. + @jade_exception def display_multisig_address( - self, - addr_type: AddressType, - multisig: MultisigDescriptor, + self, addr_type: AddressType, multisig: MultisigDescriptor ) -> str: signer_origins = [] signers = [] @@ -531,7 +513,15 @@ def display_multisig_address( ) if pubkey.deriv_path is None: raise BadArgumentError( - "Blockstream Jade can only generate addresses for multisigs with key origin derivation path information" + "Blockstream Jade can only generate addresses for multisigs with key derivation paths" + ) + + if pubkey.origin.path and not is_hardened(pubkey.origin.path[-1]): + logging.warning( + f"Final element of origin path {pubkey.origin.path} unhardened" + ) + logging.warning( + "Blockstream Jade may not be able to identify change sent back to this descriptor" ) # Tuple to derive deterministic name for the registrtion @@ -555,13 +545,13 @@ def display_multisig_address( ) paths.append(parse_path(path)) - # sort origins, signers and paths according to origins (like in _get_multisig_name) - # But, only sort if sorted_multi is used (and thus the order of xpubs is not relevant) - if multisig.is_sorted: - signer_origins, signers, paths = [ - list(a) for a in zip(*sorted(zip(signer_origins, signers, paths))) - ] - # Get a deterministic name for this multisig wallet + if multisig.is_sorted and paths[:-1] != paths[1:]: + logging.warning("Sorted multisig with different derivations per signer") + logging.warning( + "Blockstream Jade may not be able to validate change sent back to this descriptor" + ) + + # Get a deterministic name for this multisig wallet (ignoring bip67 key sorting) script_variant = self._convertAddrType(addr_type, multisig=True) multisig_name = self._get_multisig_name( script_variant, multisig.thresh, signer_origins @@ -583,262 +573,152 @@ def display_multisig_address( return str(address) + # Custom Specter method - register multisig on the Jade + @jade_exception + def register_multisig(self, descriptor: str) -> None: + + descriptor = Descriptor.from_string(descriptor) + signer_origins = [] + signers = [] + paths = [] + for key in descriptor.keys: + # Tuple to derive deterministic name for the registration + signer_origins.append((key.origin.fingerprint, key.origin.derivation)) + + # We won't include the additional path in the multisig registration + signers.append( + { + "fingerprint": key.fingerprint, + "derivation": key.derivation, + "xpub": key.key.to_string(), + "path": [], + } + ) + + # Get a deterministic name for this multisig wallet (ignoring bip67 key sorting) + if descriptor.wsh and not descriptor.sh: + addr_type = AddressType.WIT + elif descriptor.wsh and descriptor.sh: + addr_type = AddressType.SH_WIT + elif descriptor.wsh.is_legacy: + addr_type = AddressType.LEGACY + else: + raise BadArgumentError( + "The script type of the descriptor does not match any standard type." + ) + + script_variant = self._convertAddrType(addr_type, multisig=True) + threshold = descriptor.miniscript.args[0].num # hackish ... + + multisig_name = self._get_multisig_name( + script_variant, threshold, signer_origins + ) + + # 're-registering' is a no-op + self.jade.register_multisig( + self._network(), + multisig_name, + script_variant, + descriptor.is_sorted, + threshold, + signers, + ) + # Setup a new device - def setup_device(self, label="", passphrase=""): + def setup_device(self, label: str = "", passphrase: str = "") -> bool: """ - The Blockstream Jade does not support setup via software. + Blockstream Jade does not support setup via software. :raises UnavailableActionError: Always, this function is unavailable """ - raise UnavailableActionError( - "The Blockstream Jade does not support software setup" - ) + raise UnavailableActionError("Blockstream Jade does not support software setup") # Wipe this device - def wipe_device(self): + def wipe_device(self) -> bool: """ - The Blockstream Jade does not support wiping via software. + Blockstream Jade does not support wiping via software. :raises UnavailableActionError: Always, this function is unavailable """ raise UnavailableActionError( - "The Blockstream Jade does not support wiping via software" + "Blockstream Jade does not support wiping via software" ) # Restore device from mnemonic or xprv - def restore_device(self, label="", word_count=24): + def restore_device(self, label: str = "", word_count: int = 24) -> bool: """ - The Blockstream Jade does not support restoring via software. + Blockstream Jade does not support restoring via software. :raises UnavailableActionError: Always, this function is unavailable """ raise UnavailableActionError( - "The Blockstream Jade does not support restoring via software" + "Blockstream Jade does not support restoring via software" ) # Begin backup process - def backup_device(self, label="", passphrase=""): + def backup_device(self, label: str = "", passphrase: str = "") -> bool: """ - The Blockstream Jade does not support backing up via software. + Blockstream Jade does not support backing up via software. :raises UnavailableActionError: Always, this function is unavailable """ raise UnavailableActionError( - "The Blockstream Jade does not support creating a backup via software" + "Blockstream Jade does not support creating a backup via software" ) # Close the device - def close(self): + def close(self) -> None: self.jade.disconnect() # Prompt pin - def prompt_pin(self): + def prompt_pin(self) -> bool: """ - The Blockstream Jade does not need a PIN sent from the host. + Blockstream Jade does not need a PIN sent from the host. :raises UnavailableActionError: Always, this function is unavailable """ raise UnavailableActionError( - "The Blockstream Jade does not need a PIN sent from the host" + "Blockstream Jade does not need a PIN sent from the host" ) # Send pin - def send_pin(self, pin): + def send_pin(self, pin: str) -> bool: """ - The Blockstream Jade does not need a PIN sent from the host. + Blockstream Jade does not need a PIN sent from the host. :raises UnavailableActionError: Always, this function is unavailable """ raise UnavailableActionError( - "The Blockstream Jade does not need a PIN sent from the host" + "Blockstream Jade does not need a PIN sent from the host" ) # Toggle passphrase - def toggle_passphrase(self): + def toggle_passphrase(self) -> bool: """ - The Blockstream Jade does not support toggling passphrase from the host. + Blockstream Jade does not support toggling passphrase from the host. :raises UnavailableActionError: Always, this function is unavailable """ raise UnavailableActionError( - "The Blockstream Jade does not support toggling passphrase from the host" + "Blockstream Jade does not support toggling passphrase from the host" ) - def _blind(self, pset, seed: bytes = None): - if seed is None: - seed = pset.unknown.get(b"\xfc\x07specter\x00", os.urandom(32)) - txseed = pset.txseed(seed) - # assign blinding factors to all outputs - blinding_outs = [] - commitments = [] - # because we do sha once (cause taproot), and they want sha twice - hash_prevouts = hashes.sha256(pset.blinded_tx.hash_prevouts()) - last_i = 0 - last_commitment = {} - for i, out in py_enumerate(pset.outputs): - # skip ones where we don't need blinding - if out.blinding_pubkey is None: - commitments.append(None) - continue - commitment = self.jade.get_commitments( - bytes(reversed(out.asset)), out.value, hash_prevouts, i, vbf=None - ) - commitment["blinding_key"] = out.blinding_pubkey - commitments.append(commitment) - last_i = i - last_commitment = commitments[-1] - out.asset_blinding_factor = commitment["abf"] - out.value_blinding_factor = commitment["vbf"] - blinding_outs.append(out) - if len(blinding_outs) == 0: - raise Exception("Nothing to blind") - # calculate last vbf - vals = [] - abfs = [] - vbfs = [] - for sc in pset.inputs + blinding_outs: - value = sc.value if sc.value is not None else sc.utxo.value - asset = sc.asset or sc.utxo.asset - if not (isinstance(value, int) and len(asset) == 32): - continue - vals.append(value) - abfs.append(sc.asset_blinding_factor or b"\x00" * 32) - vbfs.append(sc.value_blinding_factor or b"\x00" * 32) - last_vbf = secp256k1.pedersen_blind_generator_blind_sum( - vals, abfs, vbfs, len(vals) - len(blinding_outs) - ) - last_out = blinding_outs[-1] - new_last_commitment = self.jade.get_commitments( - bytes(reversed(last_out.asset)), - last_out.value, - hash_prevouts, - last_i, - vbf=last_vbf, - ) - # check abf didn't change - assert new_last_commitment["abf"] == last_out.asset_blinding_factor - # set new values in the last commitment - last_commitment.update(new_last_commitment) - blinding_outs[-1].value_blinding_factor = last_vbf - - # calculate commitments (surj proof etc) - in_tags = [] - in_gens = [] - for inp in pset.inputs: - if inp.asset: - in_tags.append(inp.asset) - in_gens.append(secp256k1.generator_parse(inp.utxo.asset)) - # if we have unconfidential input - elif len(inp.utxo.asset) == 32: - in_tags.append(inp.utxo.asset) - in_gens.append(secp256k1.generator_generate(inp.utxo.asset)) - - for i, out in py_enumerate(pset.outputs): - if None in [out.blinding_pubkey, out.value, out.asset_blinding_factor]: - continue - gen = secp256k1.generator_generate_blinded( - out.asset, out.asset_blinding_factor - ) - out.asset_commitment = secp256k1.generator_serialize(gen) - value_commitment = secp256k1.pedersen_commit( - out.value_blinding_factor, out.value, gen - ) - out.value_commitment = secp256k1.pedersen_commitment_serialize( - value_commitment - ) + @jade_exception + def can_sign_taproot(self) -> bool: + """ + Blockstream Jade does not currently support Taproot. - proof_seed = hashes.tagged_hash( - "liquid/surjection_proof", txseed + i.to_bytes(4, "little") - ) - proof, in_idx = secp256k1.surjectionproof_initialize( - in_tags, out.asset, proof_seed - ) - secp256k1.surjectionproof_generate( - proof, in_idx, in_gens, gen, abfs[in_idx], out.asset_blinding_factor - ) - out.surjection_proof = secp256k1.surjectionproof_serialize(proof) - del proof + :returns: False, always + """ + return False - # generate range proof - rangeproof_nonce = hashes.tagged_hash( - "liquid/range_proof", txseed + i.to_bytes(4, "little") - ) - # reblind with extra message for unblinding of change outs - extra_message = ( - out.unknown.get(b"\xfc\x07specter\x01", b"") - if out.bip32_derivations - else b"" - ) - out.reblind( - rangeproof_nonce, - extra_message=extra_message, - ) - return commitments - - def sign_pset(self, b64pset: str) -> str: - """Signs specter-desktop specific Liquid PSET transaction""" - mfp = self.get_master_fingerprint() - pset = PSET.from_string(b64pset) - commitments = self._blind(pset) - ins = [ - { - "is_witness": True, - # "input_tx": inp.non_witness_utxo.serialize(), - "script": inp.witness_script.data - if inp.witness_script - else script.p2pkh_from_p2wpkh(inp.script_pubkey).data, - "value_commitment": write_commitment(inp.utxo.value), - "path": [ - der - for der in inp.bip32_derivations.values() - if der.fingerprint == mfp - ][0].derivation, - } - for inp in pset.inputs - ] - change = [ - { - "path": [ - der - for pub, der in out.bip32_derivations.items() - if der.fingerprint == mfp - ][0].derivation, - "variant": self._get_script_type(out), - } - if out.bip32_derivations and self._get_script_type(out) is not None - else None - for out in pset.outputs - ] - rawtx = pset.blinded_tx.serialize() - - signatures = self.jade.sign_liquid_tx( - self._network(), rawtx, ins, commitments, change - ) - for i, inp in py_enumerate(pset.inputs): - inp.partial_sigs[ - [ - pub - for pub, der in inp.bip32_derivations.items() - if der.fingerprint == mfp - ][0] - ] = signatures[i] - # we must finalize here because it has different commitments and only supports singlesig - return str(finalize_psbt(pset)) - - def _get_script_type(self, out): - if out.script_pubkey.script_type() == "p2pkh": - return "pkh(k)" - elif out.script_pubkey.script_type() == "p2wpkh": - return "wpkh(k)" - elif out.script_pubkey.script_type() == "p2sh": - if out.redeem_script.script_type() == "p2wpkh": - return "sh(wpkh(k))" - # otherwise None - return None - - -def enumerate(password: str = "") -> List[Dict[str, Any]]: +def enumerate( + password: Optional[str] = None, + expert: bool = False, + chain: Chain = Chain.MAIN, + skip_unlocking=True, +) -> List[Dict[str, Any]]: results = [] def _get_device_entry(device_model: str, device_path: str) -> Dict[str, Any]: @@ -851,9 +731,13 @@ def _get_device_entry(device_model: str, device_path: str) -> Dict[str, Any]: client = None with handle_errors(common_err_msgs["enumerate"], d_data): - client = JadeClient(device_path, password, timeout=1) - d_data["fingerprint"] = client.get_master_fingerprint().hex() - + client = JadeClient( + device_path, password, expert, chain, skip_unlocking, timeout=1 + ) + # The Jade could already be unlocked upon startup (this is the only instance where unlock_required is False right now). + # But, we don't need the fingerpint then. + if client and not skip_unlocking: + d_data["fingerprint"] = client.get_master_fingerprint().hex() if client: client.close() @@ -874,9 +758,9 @@ def _get_device_entry(device_model: str, device_path: str) -> Dict[str, Any]: if verinfo is not None: results.append(_get_device_entry("jade_simulator", SIMULATOR_PATH)) - except ConnectionRefusedError as e: + except Exception as e: # If we get any sort of error do not add the simulator - logger.debug(f"Failed to connect to Jade simulator at {SIMULATOR_PATH}") - logger.debug(e) + logging.debug(f"Failed to connect to Jade simulator at {SIMULATOR_PATH}") + logging.debug(e) return results diff --git a/src/cryptoadvance/specter/devices/hwi/jadepy/__init__.py b/src/cryptoadvance/specter/devices/hwi/jadepy/__init__.py index 5079bd9b70..64e2ceb7e1 100644 --- a/src/cryptoadvance/specter/devices/hwi/jadepy/__init__.py +++ b/src/cryptoadvance/specter/devices/hwi/jadepy/__init__.py @@ -1,4 +1,4 @@ from .jade import JadeAPI from .jade_error import JadeError -__version__ = "0.0.1" +__version__ = "0.2.0" diff --git a/src/cryptoadvance/specter/devices/hwi/jadepy/jade.py b/src/cryptoadvance/specter/devices/hwi/jadepy/jade.py index 4a03dc0288..4c9b882bff 100644 --- a/src/cryptoadvance/specter/devices/hwi/jadepy/jade.py +++ b/src/cryptoadvance/specter/devices/hwi/jadepy/jade.py @@ -1,4 +1,4 @@ -import cbor +import cbor2 as cbor import hashlib import json import time @@ -9,7 +9,6 @@ import random import sys - # JadeError from .jade_error import JadeError @@ -17,10 +16,20 @@ from .jade_serial import JadeSerialImpl from .jade_tcp import JadeTCPImpl -# from .jade_ble import JadeBleImpl +# 'jade' logger +logger = logging.getLogger(__name__) +device_logger = logging.getLogger(f"{__name__}-device") + +# BLE comms backend is optional +# It relies on the BLE dependencies being available +try: + from .jade_ble import JadeBleImpl +except ImportError as e: + logger.warning(e) + logger.warning("BLE scanning/connectivity will not be available") + # Default serial connection -DEFAULT_SERIAL_DEVICE = "/dev/ttyUSB0" DEFAULT_BAUD_RATE = 115200 DEFAULT_SERIAL_TIMEOUT = 120 @@ -29,14 +38,20 @@ DEFAULT_BLE_SERIAL_NUMBER = None DEFAULT_BLE_SCAN_TIMEOUT = 60 -# 'jade' logger -logger = logging.getLogger("jade") -device_logger = logging.getLogger("jade-device") - -# Helper to map bytes-like types into hex-strings -# to make for prettier message-logging def _hexlify(data): + """ + Helper to map bytes-like types into hex-strings + to make for prettier message-logging. + + Parameters + ---------- + data : any + The object to hexlify. + - bytes or bytearrays have 'hex()' method invoked + - list and dicts (values) have this function mapped over them + - Otherwise the input is returned unchanged + """ if data is None: return None elif isinstance(data, bytes) or isinstance(data, bytearray): @@ -49,49 +64,87 @@ def _hexlify(data): return data -import requests +try: + import requests + def _http_request(params): + """ + Simple http request function which can be used when a Jade response + requires an external http call. + The default implementation used in JadeAPI._jadeRpc() below. + NOTE: Only available if the 'requests' dependency is available. -def _http_request(params): - logger.debug("_http_request: {}".format(params)) + Callers can supply their own implmentation of this call where it is required. - # Use the first non-onion url - url = [url for url in params["urls"] if not url.endswith(".onion")][0] - if params["method"] == "GET": - assert "data" not in params, "Cannot pass body to requests.get" - f = requests.get(url) - elif params["method"] == "POST": - data = json.dumps(params["data"]) - f = requests.post(url, data) + Parameters + ---------- + data : dict + A dictionary structure describing the http call to make - logger.debug("http_request received reply: {}".format(f.text)) + Returns + ------- + dict + with single key 'body', whose value is the json returned from the call - if f.status_code != 200: - logger.error("http error {} : {}".format(f.status_code, f.text)) - raise ValueError(f.status_code) + """ + logger.debug("_http_request: {}".format(params)) - assert params["accept"] == "json" - f = f.json() + # Use the first non-onion url + url = [url for url in params["urls"] if not url.endswith(".onion")][0] - return {"body": f} + if params["method"] == "GET": + assert "data" not in params, "Cannot pass body to requests.get" + + def http_call_fn(): + return requests.get(url) + + elif params["method"] == "POST": + data = json.dumps(params["data"]) + + def http_call_fn(): + return requests.post(url, data) + + else: + raise JadeError(1, "Only GET and POST methods supported", params["method"]) + + try: + f = http_call_fn() + logger.debug("http_request received reply: {}".format(f.text)) + + if f.status_code != 200: + logger.error("http error {} : {}".format(f.status_code, f.text)) + raise ValueError(f.status_code) + + assert params["accept"] == "json" + f = f.json() + except Exception as e: + logging.error(e) + f = None + + return {"body": f} + +except ImportError as e: + logger.info(e) + logger.info("Default _http_requests() function will not be available") -# -# High-Level Jade Client API -# Builds on a JadeInterface to provide a meaningful API -# -# Either: -# a) use with JadeAPI.create_[serial|ble]() as jade: -# (recommended) -# or: -# b) use JadeAPI.create_[serial|ble], then call connect() before -# using, and disconnect() when finished -# (caveat cranium) -# or: -# c) use ctor to wrap existing JadeInterface instance -# (caveat cranium) -# class JadeAPI: + """ + High-Level Jade Client API + Builds on a JadeInterface to provide a meaningful API + + Either: + a) use with JadeAPI.create_[serial|ble]() as jade: + (recommended) + or: + b) use JadeAPI.create_[serial|ble], then call connect() before + using, and disconnect() when finished + (caveat cranium) + or: + c) use ctor to wrap existing JadeInterface instance + (caveat cranium) + """ + def __init__(self, jade): assert jade is not None self.jade = jade @@ -102,44 +155,134 @@ def __enter__(self): def __exit__(self, exc_type, exc, tb): if exc_type: - logger.error("Exception causing JadeAPI context exit.") - logger.error(exc_type) - logger.error(exc) + logger.info("Exception causing JadeAPI context exit.") + logger.info(exc_type) + logger.info(exc) traceback.print_tb(tb) self.disconnect(exc_type is not None) @staticmethod def create_serial(device=None, baud=None, timeout=None): + """ + Create a JadeAPI object using the serial interface described. + + Parameters + ---------- + device : str, optional + The device identifier for the serial device. + Underlying implementation will default (to /dev/ttyUSB0) + + baud : int, optional + The communication baud rate. + Underlying implementation will default (to 115200) + + timeout : int, optional + The serial read timeout when awaiting messages. + Underlying implementation will default (to 120s) + + Returns + ------- + JadeAPI + API object configured to use given serial parameters. + NOTE: the api instance has not yet tried to contact the hw + - caller must call 'connect()' before trying to use the Jade. + """ impl = JadeInterface.create_serial(device, baud, timeout) return JadeAPI(impl) @staticmethod def create_ble(device_name=None, serial_number=None, scan_timeout=None, loop=None): + """ + Create a JadeAPI object using the BLE interface described. + NOTE: raises JadeError if BLE dependencies not installed. + + Parameters + ---------- + device_name : str, optional + The device name of the desired BLE device. + Underlying implementation will default (to 'Jade') + + serial_number : int, optional + The serial number of the desired BLE device + - used to disambiguate multiple beacons with the same 'device name' + Underlying implementation will connect to the first beacon it scans + with the matching 'device name'. + + scan_timeout : int, optional + The timeout when scanning for devices which match the device name/serial number. + Underlying implementation will default (to 60s) + + loop : optional + The asynchio event loop to use, if required. + Underlying implementation will default (to asyncio.get_event_loop()) + + Returns + ------- + JadeAPI + API object configured to use given BLE parameters. + NOTE: the api instance has not yet tried to contact the hw + - caller must call 'connect()' before trying to use the Jade. + + Raises + ------ + JadeError if BLE backend not available (ie. BLE dependencies not installed) + """ impl = JadeInterface.create_ble(device_name, serial_number, scan_timeout, loop) return JadeAPI(impl) - # Connect underlying interface def connect(self): + """ + Try to connect the underlying transport interface (eg. serial, ble, etc.) + Raises an exception on failure. + """ self.jade.connect() - # Disconnect underlying interface def disconnect(self, drain=False): + """ + Disconnect the underlying transport (eg. serial, ble, etc.) + + Parameters + ---------- + drain : bool, optional + When true log any/all remaining messages/data, otherwise silently discard. + NOTE: can prevent disconnection if data is arriving constantly. + Defaults to False. + """ self.jade.disconnect(drain) - # Drain all output from the interface def drain(self): + """ + Log any/all outstanding messages/data. + NOTE: can run indefinitely if data is arriving constantly. + """ self.jade.drain() - # Raise any returned error as an exception @staticmethod def _get_result_or_raise_error(reply): + """ + Raise any error message returned from a Jade rpc call as an exception. + + Parameters + ---------- + reply : dict + Dictionary representing a reply from a Jade rpc call. + + Returns + ------- + dict + Any nested 'result' structure, if the reply is not an error. + + Raises + ------ + JadeError + If the reply represented an error, including all details received. + """ if "error" in reply: e = reply["error"] raise JadeError(e.get("code"), e.get("message"), e.get("data")) return reply["result"] - # Helper to call wrapper interface rpc invoker def _jadeRpc( self, method, @@ -148,6 +291,45 @@ def _jadeRpc( http_request_fn=None, long_timeout=False, ): + """ + Helper to make a request/reply rpc call over the underlying transport interface. + NOTE: interface must be 'connected'. + + If the call returns an 'http_request' structure, this is handled here and the http + call is made, and the result is passed into the rpc method given in 'on reply', by + calling this function recursively. + + Parameters + ---------- + method : str + rpc method to invoke + + params : dict, optional + any parameters to pass to the rpc method + Defaults to None. + + inputid : str, optional + Any specific 'id' to use in the rpc message. + Defaults to a using a pseudo-random id generated in-situ. + + http_request_fn : function, optional + A function which accepts a dict (containing a description of the http request), makes + the described http call, and returns the body data in an element called 'body'. + Defaults to _http_request() above. + + long_timeout : bool, optional + Whether the rpc call should use an indefinitely long timeout, rather than that set on + construction. + (Useful if the call involves a non-trivial user interaction with the device.) + Defaults to False. + + Returns + ------- + dict + The reply from the rpc call. + NOTE: will return the last/final reply after a sequence of calls, where 'http_request' + was returned and remote data was fetched and passed into s subsequent call. + """ newid = inputid if inputid else str(random.randint(100000, 999999)) request = self.jade.build_request(newid, method, params) reply = self.jade.make_rpc_call(request, long_timeout) @@ -177,27 +359,150 @@ def _jadeRpc( return result - # Get version information from the hw - def get_version_info(self): - return self._jadeRpc("get_version_info") + def ping(self): + """ + RPC call to test the connection to Jade and that Jade is powered on and receiving data, and + return whether the main task is currently handling a message, handling user menu navigation + or is idle. + + NOTE: unlike all other calls this is not queued and handled in fifo order - this message is + handled immediately and the response sent as quickly as possible. This call does not block. + If this call is made in parallel with Jade processing other messages, the replies may be + out of order (although the message 'id' should still be correct). Use with caution. + + Returns + ------- + 0 if the main task is currently idle + 1 if the main task is handling a client message + 2 if the main task is handling user ui menu navigation + """ + return self._jadeRpc("ping") + + def get_version_info(self, nonblocking=False): + """ + RPC call to fetch summary details pertaining to the hardware unit and running firmware. + + Parameters + ---------- + nonblocking : bool + If True message will be handled immediately (see also ping()) *experimental feature* + + Returns + ------- + dict + Contains keys for various info describing the hw and running fw + """ + params = {"nonblocking": True} if nonblocking else None + return self._jadeRpc("get_version_info", params) - # Add client entropy to the hw rng def add_entropy(self, entropy): + """ + RPC call to add client entropy into the unit RNG entropy pool. + + Parameters + ---------- + entropy : bytes + Bytes to fold into the hw entropy pool. + + Returns + ------- + bool + True on success + """ params = {"entropy": entropy} return self._jadeRpc("add_entropy", params) - # OTA new firmware - def ota_update(self, fwcmp, fwlen, chunksize, cb): - + def set_epoch(self, epoch=None): + """ + RPC call to set the current time epoch value, required for TOTP use. + NOTE: The time is lost on each power-down and must be reset on restart/reconnect before + TOTP can be used. + + Parameters + ---------- + epoch : int, optional + Current epoch value, in seconds. Defaults to int(time.time()) value. + + Returns + ------- + bool + True on success + """ + params = {"epoch": epoch if epoch is not None else int(time.time())} + return self._jadeRpc("set_epoch", params) + + def logout(self): + """ + RPC call to logout of any wallet loaded on the Jade unit. + Any key material is freed and zero'd. + Call always returns true. + + Returns + ------- + bool + True + """ + return self._jadeRpc("logout") + + def ota_update(self, fwcmp, fwlen, chunksize, fwhash=None, patchlen=None, cb=None): + """ + RPC call to attempt to update the unit's firmware. + + Parameters + ---------- + fwcmp : bytes + The compressed firmware image to upload to the Jade unit. Can be a full firmware or + and incremental diff to be applied to the currently running firmware image. + fwlen : int + The size of the new complete (uncompressed) firmware image (after any delta is applied). + chunksize : int + The size of the chunks used to upload the compressed firmware. Each chunk is uploaded + and ack'd by the hw unit. + The maximum supported chunk size is given in the version info data, under the key + 'JADE_OTA_MAX_CHUNK'. + fwhash: 32-bytes, optional + The sha256 hash of the full uncompressed final firmware image. In the case of a full + firmware upload this should be the hash of the uncompressed file. In the case of a + delta update this is the hash of the expected final image - ie. the existing firmware + with the uploaded delta applied. ie. it is a verification of the fw image Jade will try + to boot. Optional for backward-compatibility - may become mandatory in a future release. + patchlen: int, optional + If the compressed firmware bytes are an incremental diff to be applied to the running + firmware image, this is the size of that patch when uncompressed. + Defaults to None, implying the compressed data is a full firmware image upload. + (Compare with fwlen - the size of the final fw image.) + cb : function, optional + Callback function accepting two integers - the amount of compressed firmware sent thus + far, and the total length of the compressed firmware to send. + If passed, this function is invoked each time a fw chunk is successfully uploaded and + ack'd by the hw, to notify of upload progress. + Defaults to None, and nothing is called to report upload progress. + + Returns + ------- + bool + True if no errors were reported - on next restart the hw unit will attempt to boot the + new firmware. + """ + + # Compute the sha256 hash of the compressed file being uploaded cmphasher = hashlib.sha256() cmphasher.update(fwcmp) cmphash = cmphasher.digest() cmplen = len(fwcmp) # Initiate OTA + ota_method = "ota" params = {"fwsize": fwlen, "cmpsize": cmplen, "cmphash": cmphash} - result = self._jadeRpc("ota", params) + if fwhash is not None: + params["fwhash"] = fwhash + + if patchlen is not None: + ota_method = "ota_delta" + params["patchsize"] = patchlen + + result = self._jadeRpc(ota_method, params) assert result is True # Write binary chunks @@ -216,12 +521,97 @@ def ota_update(self, fwcmp, fwlen, chunksize, cb): # All binary data uploaded return self._jadeRpc("ota_complete") - # Run (debug) healthcheck on the hw def run_remote_selfcheck(self): + """ + RPC call to run in-built tests. + NOTE: Only available in a DEBUG build of the firmware. + + Returns + ------- + int + Time in ms for the internal tests to run, as measured on the hw. + ie. excluding any messaging overhead + """ return self._jadeRpc("debug_selfcheck", long_timeout=True) - # Set the (debug) mnemonic + def capture_image_data(self, check_qr=False): + """ + RPC call to capture raw image data from the camera. + See also scan_qr() below. + NOTE: Only available in a DEBUG build of the firmware. + + Parameters + ---------- + check_qr : bool, optional + If True only images which contain a valid qr code are captured and returned. + If False, any image is considered valid and is returned. + Defaults to False + + Returns + ------- + bytes + Raw image data from the camera framebuffer + """ + params = {"check_qr": check_qr} + return self._jadeRpc("debug_capture_image_data", params) + + def scan_qr(self, image): + """ + RPC call to scan a passed image and return any data extracted from any qr image. + Exercises the camera image capture, but ignores result and uses passed image instead. + See also capture_image_data() above. + NOTE: Only available in a DEBUG build of the firmware. + + Parameters + ---------- + image : bytes + The image data (as obtained from capture_image_data() above). + + Returns + ------- + bytes + String or byte data obtained from the image (via qr code) + """ + params = {"image": image} + return self._jadeRpc("debug_scan_qr", params) + + def clean_reset(self): + """ + RPC call to clean/reset memory and storage, as much as is practical. + NOTE: Only available in a DEBUG build of the firmware. + + Returns + ------- + bool + True on success. + """ + return self._jadeRpc("debug_clean_reset") + def set_mnemonic(self, mnemonic, passphrase=None, temporary_wallet=False): + """ + RPC call to set the wallet mnemonic (in RAM only - flash storage is untouched). + NOTE: Only available in a DEBUG build of the firmware. + + Parameters + ---------- + mnemonic : str + The wallet mnemonic to set. + + passphrase : str, optional + Any bip39 passphrase to apply. + Defaults to None. + + temporary_wallet : bool, optional + Whether to treat this wallet/mnemonic as an 'Emergency Restore' temporary wallet, as + opposed to one successfully loaded from the flash storage. + NOTE: in either case the wallet is only set in RAM, and flash storage is not affected. + Defaults to False. + + Returns + ------- + bool + True on success. + """ params = { "mnemonic": mnemonic, "passphrase": passphrase, @@ -229,13 +619,81 @@ def set_mnemonic(self, mnemonic, passphrase=None, temporary_wallet=False): } return self._jadeRpc("debug_set_mnemonic", params) - # Set the (debug) seed - def set_seed(self, seed, temporary_wallet=False): - params = {"seed": seed, "temporary_wallet": temporary_wallet} + def set_seed(self, seed): + """ + RPC call to set the wallet seed. + NOTE: Only available in a DEBUG build of the firmware. + NOTE: Setting a seed always sets a 'temporary' wallet. + + Parameters + ---------- + seed : bytes + The wallet seed to set as a temporary wallet (cannot be persisted in flash). + + Returns + ------- + bool + True on success. + """ + params = {"seed": seed} return self._jadeRpc("debug_set_mnemonic", params) - # Override the pinserver details on the hww + def get_bip85_bip39_entropy(self, num_words, index, pubkey): + """ + RPC call to fetch encrypted bip85-bip39 entropy. + NOTE: Only available in a DEBUG build of the firmware. + + Parameters + ---------- + num_words : int + The number of words the entropy is required to produce. + + index : int + The index to use in the bip32 path to calcuate the entropy. + + pubkey: 33-bytes + The host ephemeral pubkey to use to generate a shared ecdh secret to use as an AES key + to encrypt the returned entropy. + + Returns + ------- + dict + pubkey - 33-bytes, Jade's ephemeral pubkey used to generate a shared ecdh secret used as + an AES key to encrypt the returned entropy + encrypted - bytes, the requested bip85 bip39 entropy, AES encrypted with the first key + derived from the ecdh shared secret, prefixed with the iv + hmac - 32-bytes, the hmac of the encrypted buffer, using the second key derived from the + ecdh shared secret + """ + params = {"num_words": num_words, "index": index, "pubkey": pubkey} + return self._jadeRpc("get_bip85_bip39_entropy", params) + def set_pinserver(self, urlA=None, urlB=None, pubkey=None, cert=None): + """ + RPC call to explicitly set (override) the details of the blind pinserver used to + authenticate the PIN entered on the Jade unit. + This data is recorded in the hw flash, and returned to the caller when authenticating + (in auth_user(), below). + + Parameters + ---------- + urlA : str, optional + The primary url of the pinserver to use. + + urlB : str, optional + Any secondary url of the pinserver to use. + + pubkey : bytes, optional + The public key used to verify pinserver signed payloads. + + cert : bytes, optional + Any additional certificate required to verify the pinserver identity. + + Returns + ------- + bool + True on success. + """ params = {} if urlA is not None or urlB is not None: params["urlA"] = urlA @@ -246,35 +704,244 @@ def set_pinserver(self, urlA=None, urlB=None, pubkey=None, cert=None): params["certificate"] = cert return self._jadeRpc("update_pinserver", params) - # Reset the pinserver details on the hww to their defaults def reset_pinserver(self, reset_details, reset_certificate): + """ + RPC call to reset any formerly overidden pinserver details to their defauts. + + Parameters + ---------- + reset_details : bool, optional + If set, any overidden urls and pubkey are reset to their defaults. + + reset_certificate : bool, optional + If set, any additional certificate is reset (to None). + + Returns + ------- + bool + True on success. + """ params = { "reset_details": reset_details, "reset_certificate": reset_certificate, } return self._jadeRpc("update_pinserver", params) - # Trigger user authentication on the hw - # Involves pinserver handshake - def auth_user(self, network, http_request_fn=None): - params = {"network": network} + def auth_user(self, network, http_request_fn=None, epoch=None): + """ + RPC call to authenticate the user on the hw device, for using with the network provided. + + Parameters + ---------- + network : str + The name of the network intended for use - eg. 'mainnet', 'liquid', 'testnet' etc. + This is verified against the networks allowed on the hardware. + + http_request_fn : function, optional + Optional http-request function to pass http requests to the Jade pinserver. + Default behaviour is to use the '_http_request()' function which defers to the + 'requests' module. + If the 'reqests' module is not available, no default http-request function is created, + and one must be supplied here. + + epoch : int, optional + Current epoch value, in seconds. Defaults to int(time.time()) value. + + Returns + ------- + bool + True is returned immediately if the hw is already unlocked for use on the given network. + True if the PIN is entered and verified with the remote blind pinserver. + False if the PIN entered was incorrect. + """ + params = { + "network": network, + "epoch": epoch if epoch is not None else int(time.time()), + } return self._jadeRpc( "auth_user", params, http_request_fn=http_request_fn, long_timeout=True ) - # Get xpub given a path + def register_otp(self, otp_name, otp_uri): + """ + RPC call to register a new OTP record on the hw device. + + Parameters + ---------- + otp_name : str + An identifying name for this OTP record + + otp_uri : str + The uri of this OTP record - must begin 'otpauth://' + + Returns + ------- + bool + True if the OTP uri was validated and persisted on the hw + """ + params = {"name": otp_name, "uri": otp_uri} + return self._jadeRpc("register_otp", params) + + def get_otp_code(self, otp_name, value_override=None): + """ + RPC call to fetch a new OTP code from the hw device. + + Parameters + ---------- + otp_name : str + An identifying name for the OTP record to use + + value_override : int + An overriding HOTP counter or TOTP timestamp to use. + NOTE: Only available in a DEBUG build of the firmware. + + Returns + ------- + bool + True if the OTP uri was validated and persisted on the hw + """ + params = {"name": otp_name} + if value_override is not None: + params["override"] = value_override + return self._jadeRpc("get_otp_code", params) + def get_xpub(self, network, path): + """ + RPC call to fetch an xpub for the given bip32 path for the given network. + + Parameters + ---------- + network : str + Network to which the xpub applies - eg. 'mainnet', 'liquid', 'testnet', etc. + + path : [int] + bip32 path for which the xpub should be generated. + + Returns + ------- + str + base58 encoded xpub + """ params = {"network": network, "path": path} return self._jadeRpc("get_xpub", params) - # Get registered multisig wallets def get_registered_multisigs(self): + """ + RPC call to fetch brief summaries of any multisig wallets registered to this signer. + + Returns + ------- + dict + Brief description of registered multisigs, keyed by registration name. + Each entry contains keys: + variant - str, script type, eg. 'sh(wsh(multi(k)))' + sorted - boolean, whether bip67 key sorting is applied + threshold - int, number of signers required,N + num_signers - total number of signatories, M + master_blinding_key - 32-bytes, any liquid master blinding key for this wallet + """ return self._jadeRpc("get_registered_multisigs") - # Register a multisig wallet + def get_registered_multisig(self, multisig_name, as_file=False): + """ + RPC call to fetch details of a named multisig wallet registered to this signer. + NOTE: the multisig wallet must have been registered with firmware v1.0.23 or later + for the full signer details to be persisted and available. + + Parameters + ---------- + multisig_name : string + Name of multsig registration record to return. + + as_file : string, optional + If true the flat file format is returned, otherwise structured json is returned. + Defaults to false. + + Returns + ------- + dict + Description of registered multisig wallet identified by registration name. + Contains keys: + is_file is true: + multisig_file - str, the multisig file as produced by several wallet apps. + eg: + Name: MainWallet + Policy: 2 of 3 + Format: P2WSH + Derivation: m/48'/0'/0'/2' + + B237FE9D: xpub6E8C7BX4c7qfTsX7urnXggcAyFuhDmYLQhwRwZGLD9maUGWPinuc9k96ej... + 249192D2: xpub6EbXynW6xjYR3crcztum6KzSWqDJoAJQoovwamwVnLaCSHA6syXKPnJo6U... + 67F90FFC: xpub6EHuWWrYd8bp5FS1XAZsMPkmCqLSjpULmygWqAqWRCCjSWQwz6ntq5KnuQ... + + is_file is false: + multisig_name - str, name of multisig registration + variant - str, script type, eg. 'sh(wsh(multi(k)))' + sorted - boolean, whether bip67 key sorting is applied + threshold - int, number of signers required,N + master_blinding_key - 32-bytes, any liquid master blinding key for this wallet + signers - dict containing keys: + fingerprint - 4 bytes, origin fingerprint + derivation - [int], bip32 path from origin to signer xpub provided + xpub - str, base58 xpub of signer + path - [int], any fixed path to always apply after the xpub - usually empty. + + """ + params = {"multisig_name": multisig_name, "as_file": as_file} + return self._jadeRpc("get_registered_multisig", params) + def register_multisig( - self, network, multisig_name, variant, sorted_keys, threshold, signers + self, + network, + multisig_name, + variant, + sorted_keys, + threshold, + signers, + master_blinding_key=None, ): + """ + RPC call to register a new multisig wallet, which must contain the hw signer. + A registration name is provided - if it already exists that record is overwritten. + + Parameters + ---------- + network : string + Network to which the multisig should apply - eg. 'mainnet', 'liquid', 'testnet', etc. + + multisig_name : string + Name to use to identify this multisig wallet registration record. + If a registration record exists with the name given, that record is overwritten. + + variant : str + The script type - one of 'sh(multi(k))', 'wsh(multi(k))', 'sh(wsh(multi(k)))' + + sorted_keys : bool + Whether this is a 'sortedmulti()' wallet - ie. whether to apply bip67 sorting to the + pubkeys when generating redeem scripts. + + threshold : int + Number of signers required. + + signers : [dict] + Description of signers - should include keys: + - 'fingerprint' - 4 bytes, origin fingerprint + - 'derivation' - [int], bip32 path from origin to signer xpub provided + - 'xpub' - str, base58 xpub of signer - will be verified for hw unit signer + - 'path' - [int], any fixed path to always apply after the xpub - usually empty. + + master_blinding_key : 32-bytes, optional + The master blinding key to use for this multisig wallet on liquid. + Optional, defaults to None. + Logically mandatory when 'network' indicates a liquid network and the Jade is to be + used to generate confidential addresses, blinding keys, blinding nonces, asset blinding + factors or output commitments. + + Returns + ------- + bool + True on success, implying the mutisig wallet can now be used. + """ params = { "network": network, "multisig_name": multisig_name, @@ -283,18 +950,146 @@ def register_multisig( "sorted": sorted_keys, "threshold": threshold, "signers": signers, + "master_blinding_key": master_blinding_key, }, } return self._jadeRpc("register_multisig", params) - # Get receive-address for parameters + def register_multisig_file(self, multisig_file): + """ + RPC call to register a new multisig wallet, which must contain the hw signer. + A registration file is provided - as produced my several wallet apps. + + Parameters + ---------- + multisig_file : string + The multisig file as produced by several wallet apps. + eg: + Name: MainWallet + Policy: 2 of 3 + Format: P2WSH + Derivation: m/48'/0'/0'/2' + + B237FE9D: xpub6E8C7BX4c7qfTsX7urnXggcAyFuhDmYLQhwRwZGLD9maUGWPinuc9k96ejhEQ1DCk... + 249192D2: xpub6EbXynW6xjYR3crcztum6KzSWqDJoAJQoovwamwVnLaCSHA6syXKPnJo6U3bVeGde... + 67F90FFC: xpub6EHuWWrYd8bp5FS1XAZsMPkmCqLSjpULmygWqAqWRCCjSWQwz6ntq5KnuQnL23No2... + + Returns + ------- + bool + True on success, implying the mutisig wallet can now be used. + """ + params = {"multisig_file": multisig_file} + return self._jadeRpc("register_multisig", params) + + def register_descriptor( + self, network, descriptor_name, descriptor_script, datavalues=None + ): + """ + RPC call to register a new descriptor wallet, which must contain the hw signer. + A registration name is provided - if it already exists that record is overwritten. + + Parameters + ---------- + network : string + Network to which the multisig should apply - eg. 'mainnet', 'liquid', 'testnet', etc. + + descriptor_name : string + Name to use to identify this descriptor wallet registration record. + If a registration record exists with the name given, that record is overwritten. + + Returns + ------- + bool + True on success, implying the descriptor wallet can now be used. + """ + params = { + "network": network, + "descriptor_name": descriptor_name, + "descriptor": descriptor_script, + "datavalues": datavalues, + } + return self._jadeRpc("register_descriptor", params) + def get_receive_address( - self, *args, recovery_xpub=None, csv_blocks=0, variant=None, multisig_name=None + self, + *args, + recovery_xpub=None, + csv_blocks=0, + variant=None, + multisig_name=None, + descriptor_name=None, + confidential=None, ): + """ + RPC call to generate, show, and return an address for the given path. + The call has three forms. + + Parameters + ---------- + network: str + Network to which the address should apply - eg. 'mainnet', 'liquid', 'testnet', etc. + + Then either: + + 1. Blockstream Green (multisig shield) addresses + subaccount : int + Blockstream Green subaccount + + branch : int + Blockstream Green derivation branch + + pointer : int + Blockstream Green address pointer + + recovery_xpub : str, optional + xpub of recovery key for 2of3 subaccounts. Otherwise should be omitted. + Defaults to None (ie. not a 2of3 subaccount). + + csv_blocks : int, optional + Number of blocks to include in csv redeem script, if this is a csv-enabled account. + Otherwise should be omitted. + Defaults to 0 (ie. does not apply/not a csv-enabled account.) + + 2. Generic single-sig addresses + path: [int] + bip32 path for which the xpub should be generated. + + variant: str + The script type - one of 'pkh(k)', 'wpkh(k)', 'sh(wpkh(k))' + + 3. Generic multisig addresses + paths: [[int]] + bip32 path suffixes, one for each signer, applied as a suffix to the registered + signer path. Usually these path suffixes will all be identical. + + multisig_name : str + The name of the registered multisig wallet record used to generate the address. + + 4. Descriptor wallet addresses + branch : int + Multi-path derivation branch, usually 0. + + pointer : int + Path index to descriptor + + descriptor_name : str + The name of the registered descriptor wallet record used to generate the address. + + Returns + ------- + str + The address generated for the given parameters. + + """ if multisig_name is not None: assert len(args) == 2 keys = ["network", "paths", "multisig_name"] args += (multisig_name,) + elif descriptor_name is not None: + assert len(args) == 3 + keys = ["network", "branch", "pointer", "descriptor_name"] + args += (descriptor_name,) elif variant is not None: assert len(args) == 2 keys = ["network", "path", "variant"] @@ -310,9 +1105,13 @@ def get_receive_address( "csv_blocks", ] args += (recovery_xpub, csv_blocks) - return self._jadeRpc("get_receive_address", dict(zip(keys, args))) - # Sign a message + params = dict(zip(keys, args)) + if confidential is not None: + params["confidential"] = confidential + + return self._jadeRpc("get_receive_address", params) + def sign_message( self, path, @@ -321,6 +1120,34 @@ def sign_message( ae_host_commitment=None, ae_host_entropy=None, ): + """ + RPC call to format and sign the given message, using the given bip32 path. + Supports RFC6979 and anti-exfil signatures. + + Parameters + ---------- + path : [int] + bip32 path for which the signature should be generated. + + message : str + Message string to format and sign. + + ae_host_commitment : 32-bytes, optional + The host-commitment to use for Antil-Exfil signatures + + ae_host_entropy : 32-bytes, optional + The host-entropy to use for Antil-Exfil signatures + + Returns + ------- + 1. Legacy/RFC6979 signatures + str + base64-encoded signature + + 2. Anti-exfil signatures + (bytes, str) + signer-commitment, base64-encoded signature + """ if use_ae_signatures: # Anti-exfil protocol: # We send the signing request and receive the signer-commitment in @@ -340,68 +1167,339 @@ def sign_message( params = {"path": path, "message": message} return self._jadeRpc("sign_message", params) - # Get a Liquid master blinding key - def get_master_blinding_key(self): - return self._jadeRpc("get_master_blinding_key") - - # Get a Liquid public blinding key for a given script - def get_blinding_key(self, script): - params = {"script": script} + def sign_message_file(self, message_file): + """ + RPC call to format and sign the given message, using the given bip32 path. + A message file is provided - as produced by eg. Specter wallet. + Supports RFC6979 only. + + Parameters + ---------- + message_file : str + Message file to parse and produce signature for. + eg: 'signmessage m/84h/0h/0h/0/0 ascii:this is a test message' + + Returns + ------- + str + base64-encoded RFC6979 signature + """ + params = {"message_file": message_file} + return self._jadeRpc("sign_message", params) + + def get_identity_pubkey(self, identity, curve, key_type, index=0): + """ + RPC call to fetch a pubkey for the given identity (slip13/slip17). + NOTE: this api returns an uncompressed public key + + Parameters + ---------- + identity : str + Identity string to format and sign. For example ssh://satoshi@bitcoin.org + + curve : str + Name of curve to use - currently only 'nist256p1' is supported + + key_type : str + Key derivation type - must be either 'slip-0013' for an identity pubkey, or 'slip-0017' + for an ecdh pubkey. + + index : int, optional + Index number (if require multiple keys/sigs per identity) + Defaults to 0 + + Returns + ------- + 65-bytes + Uncompressed public key for the given identity and index. + Consistent with 'sign_identity' or 'get_identity_shared_key', depending on the + 'key_type'. + + """ + params = { + "identity": identity, + "curve": curve, + "type": key_type, + "index": index, + } + return self._jadeRpc("get_identity_pubkey", params) + + def get_identity_shared_key(self, identity, curve, their_pubkey, index=0): + """ + RPC call to fetch a SLIP-0017 shared ecdh key for the identity and counterparty public key. + NOTE: this api takes an uncompressed public key + + Parameters + ---------- + identity : str + Identity string to format and sign. For example ssh://satoshi@bitcoin.org + + curve : str + Name of curve to use - currently only 'nist256p1' is supported + + their_pubkey : 65-bytes + The counterparty's uncompressed public key + + index : int, optional + Index number (if require multiple keys/sigs per identity) + Defaults to 0 + + Returns + ------- + 32-bytes + The shared ecdh key for the given identity and cpty public key + Consistent with 'get_identity_pubkey' with 'key_type=slip-0017' + """ + params = { + "identity": identity, + "curve": curve, + "index": index, + "their_pubkey": their_pubkey, + } + return self._jadeRpc("get_identity_shared_key", params) + + def sign_identity(self, identity, curve, challenge, index=0): + """ + RPC call to authenticate the given identity through a challenge. + Supports RFC6979. + Returns the signature and the associated SLIP-0013 pubkey + NOTE: this api returns an uncompressed public key + + Parameters + ---------- + identity : str + Identity string to format and sign. For example ssh://satoshi@bitcoin.org + + curve : str + Name of curve to use - currently only 'nist256p1' is supported + + challenge : bytes + Challenge bytes to sign + + index : int, optional + Index number (if require multiple keys/sigs per identity) + Defaults to 0 + + Returns + ------- + dict + Contains keys: + pubkey - 65-bytes, the uncompressed SLIP-0013 public key, consistent with + 'get_identity_pubkey' with 'key_type=slip-0013' + signature - 65-bytes, RFC6979 deterministic signature, prefixed with 0x00 + """ + params = { + "identity": identity, + "curve": curve, + "index": index, + "challenge": challenge, + } + return self._jadeRpc("sign_identity", params) + + def get_master_blinding_key(self, only_if_silent=False): + """ + RPC call to fetch the master (SLIP-077) blinding key for the hw signer. + May block temporarily to request the user's permission to export. Passing 'only_if_silent' + causes the call to return the 'denied' error if it would normally ask the user. + NOTE: the master blinding key of any registered multisig wallets can be obtained from + the result of `get_registered_multisigs()`. + + Parameters + ---------- + only_if_silent : boolean, optional + If True Jade will return the denied error if it would normally ask the user's permission + to export the master blinding key. Passing False (or letting default) may block while + asking the user to confirm the export on Jade. + + Returns + ------- + 32-bytes + SLIP-077 master blinding key + """ + params = {"only_if_silent": only_if_silent} + return self._jadeRpc("get_master_blinding_key", params) + + def get_blinding_key(self, script, multisig_name=None): + """ + RPC call to fetch the public blinding key for the hw signer. + + Parameters + ---------- + script : bytes + The script for which the public blinding key is required. + + multisig_name : str, optional + The name of any registered multisig wallet for which to fetch the blinding key. + Defaults to None + + Returns + ------- + 33-bytes + Public blinding key for the passed script. + """ + params = {"script": script, "multisig_name": multisig_name} return self._jadeRpc("get_blinding_key", params) - # Get the shared secret to unblind a tx, given the receiving script on - # our side and the pubkey of the sender (sometimes called "nonce" in - # Liquid). Optionally fetch our blinding pubkey also. - def get_shared_nonce(self, script, their_pubkey, include_pubkey=False): + def get_shared_nonce( + self, script, their_pubkey, include_pubkey=False, multisig_name=None + ): + """ + RPC call to get the shared secret to unblind a tx, given the receiving script and + the pubkey of the sender (sometimes called "blinding nonce" in Liquid). + Optionally fetch the hw signer's public blinding key also. + + Parameters + ---------- + script : bytes + The script for which the blinding nonce is required. + + their_pubkey : 33-bytes + The counterparty public key. + + include_pubkey : bool, optional + Whether to also return the wallet's public blinding key. + Defaults to False. + + multisig_name : str, optional + The name of any registered multisig wallet for which to fetch the blinding nonce. + Defaults to None + + Returns + ------- + 1. include_pubkey is False + 33-bytes + Public blinding nonce for the passed script and counterparty public key. + + 2. include_pubkey is True + dict + Contains keys: + shared_nonce - 32-bytes, public blinding nonce for the passed script as above. + blinding_key - 33-bytes, public blinding key for the passed script. + """ params = { "script": script, "their_pubkey": their_pubkey, "include_pubkey": include_pubkey, + "multisig_name": multisig_name, } return self._jadeRpc("get_shared_nonce", params) - # Get a "trusted" blinding factor to blind an output. Normally the blinding - # factors are generated and returned in the `get_commitments` call, but - # for the last output the VBF must be generated on the host side, so this - # call allows the host to get a valid ABF to compute the generator and - # then the "final" VBF. Nonetheless, this call is kept generic, and can - # also generate VBFs, thus the "type" parameter. - # `hash_prevouts` is computed as specified in BIP143 (double SHA of all - # the outpoints being spent as input. It's not checked right away since - # at this point Jade doesn't know anything about the tx we are referring - # to. It will be checked later during `sign_liquid_tx`. - # `output_index` is the output we are trying to blind. - # `type` can either be "ASSET" or "VALUE" to generate ABFs or VBFs. - def get_blinding_factor(self, hash_prevouts, output_index, type): + def get_blinding_factor( + self, hash_prevouts, output_index, bftype, multisig_name=None + ): + """ + RPC call to get deterministic blinding factors to blind an output. + Predicated on the host calculating the 'hash_prevouts' value correctly. + Can fetch abf, vbf, or both together. + + Parameters + ---------- + + hash_prevouts : 32-bytes + This value should be computed by the host as specified in bip143. + It is not verified by Jade, since at this point Jade does not have the tx in question. + + output_index : int + The index of the output we are trying to blind + + bftype : str + Can be "ASSET", "VALUE", or "ASSET_AND_VALUE", to generate abf, vbf, or both. + + multisig_name : str, optional + The name of any registered multisig wallet for which to fetch the blinding factor. + Defaults to None + + Returns + ------- + 32-bytes or 64-bytes + The blinding factor for "ASSET" and "VALUE" requests, or both concatenated abf|vbf + ie. the first 32 bytes being abf, the second 32 bytes being vbf. + """ params = { "hash_prevouts": hash_prevouts, "output_index": output_index, - "type": type, + "type": bftype, + "multisig_name": multisig_name, } return self._jadeRpc("get_blinding_factor", params) - # Generate the blinding factors and commitments for a given output. - # Can optionally get a "custom" VBF, normally used for the last - # input where the VBF is not random, but generated accordingly to - # all the others. - # `hash_prevouts` and `output_index` have the same meaning as in - # the `get_blinding_factor` call. - # NOTE: the `asset_id` should be passed as it is normally displayed, so - # reversed compared to the "consensus" representation. - def get_commitments(self, asset_id, value, hash_prevouts, output_index, vbf=None): + def get_commitments( + self, asset_id, value, hash_prevouts, output_index, vbf=None, multisig_name=None + ): + """ + RPC call to generate deterministic blinding factors and commitments for a given output. + Can optionally get a "custom" VBF, normally used for the last input where the vbf is not + computed here, but generated on the host according to all the other values. + The commitments generated here should be passed back into `sign_liquid_tx()`. + + Parameters + ---------- + asset_id : 32-bytes + asset_id as usually displayed - ie. reversed compared to network/consensus order + + value : int + value in 'satoshi' or equivalent atomic integral unit + + hash_prevouts : 32-bytes + This value is computed as specified in bip143. + It is verified immediately since at this point Jade doesn't have the tx in question. + It will be checked later during `sign_liquid_tx()`. + + output_index : int + The index of the output we are trying to blind + + vbf : 32-bytes, optional + The vbf to use, in preference to deterministically generating one in this call. + + multisig_name : str, optional + The name of any registered multisig wallet for which to fetch the blinding factor. + Defaults to None + + Returns + ------- + dict + Containing the blinding factors and output commitments. + """ params = { "asset_id": asset_id, "value": value, "hash_prevouts": hash_prevouts, "output_index": output_index, + "vbf": vbf, + "multisig_name": multisig_name, } - if vbf is not None: - params["vbf"] = vbf return self._jadeRpc("get_commitments", params) - # Common code for sending btc- and liquid- tx-inputs and receiving the - # signatures. Handles standard EC and AE signing schemes. def _send_tx_inputs(self, base_id, inputs, use_ae_signatures): + """ + Helper call to send the tx inputs to Jade for signing. + Handles legacy RFC6979 signatures, as well as the Anti-Exfil protocol. + + Parameters + ---------- + base_id : int + The ids of the messages sent will be increments from this base id. + + inputs : [dict] + The tx inputs - see `sign_tx()` / `sign_liquid_tx()` for details. + + use_ae_signatures : bool + Whether to use the anti-exfil protocol to generate the signatures + + Returns + ------- + 1. if use_ae_signatures is False + [bytes] + An array of signatures corresponding to the array of inputs passed. + The signatures are in DER format with the sighash appended. + 'None' placeholder elements are used for inputs not requiring a signature. + + 2. if use_ae_signatures is True + [(32-bytes, bytes)] + An array of pairs of signer-commitments and signatures corresponding to the inputs. + The signatures are in DER format with the sighash appended. + (None, None) placeholder elements are used for inputs not requiring a signature. + """ if use_ae_signatures: # Anti-exfil protocol: # We send one message per input (which includes host-commitment *but @@ -414,7 +1512,7 @@ def _send_tx_inputs(self, base_id, inputs, use_ae_signatures): host_ae_entropy_values = [] for txinput in inputs: # ae-protocol - do not send the host entropy immediately - txinput = txinput.copy() # shallow copy + txinput = txinput.copy() if txinput else {} # shallow copy host_ae_entropy_values.append(txinput.pop("ae_host_entropy", None)) base_id += 1 @@ -446,6 +1544,9 @@ def _send_tx_inputs(self, base_id, inputs, use_ae_signatures): # Send all n inputs requests = [] for txinput in inputs: + if txinput is None: + txinput = {} + base_id += 1 msg_id = str(base_id) request = self.jade.build_request(msg_id, "tx_input", txinput) @@ -464,10 +1565,107 @@ def _send_tx_inputs(self, base_id, inputs, use_ae_signatures): assert len(signatures) == len(inputs) return signatures - # Sign a Liquid txn def sign_liquid_tx( - self, network, txn, inputs, commitments, change, use_ae_signatures=False + self, + network, + txn, + inputs, + commitments, + change, + use_ae_signatures=False, + asset_info=None, + additional_info=None, ): + """ + RPC call to sign a liquid transaction. + + Parameters + ---------- + network : str + Network to which the txn should apply - eg. 'liquid', 'liquid-testnet', etc. + + txn : bytes + The transaction to sign + + inputs : [dict] + The tx inputs. + If signing this input, should contain keys: + is_witness, bool - whether this is a segwit input + script, bytes- the redeem script + path, [int] - the bip32 path to sign with + value_commitment, 33-bytes - The value commitment of ths input + + This is optional if signing this input: + sighash, int - The sighash to use, defaults to 0x01 (SIGHASH_ALL) + + These are only required for Anti-Exfil signatures: + ae_host_commitment, 32-bytes - The host-commitment for Anti-Exfil signatures + ae_host_entropy, 32-bytes - The host-entropy for Anti-Exfil signatures + + These are only required for advanced transactions, eg. swaps, and only when the + inputs need unblinding. + Not needed for vanilla send-payment/redeposit etc: + abf, 32-bytes - asset blinding factor + asset_id, 32-bytes - the unblinded asset-id + asset_generator, 33-bytes - the (blinded) asset-generator + vbf, 32-bytes - the value blinding factor + value, int - the unblinded sats value of the input + + If not signing this input a null or an empty dict can be passed. + + commitments : [dict] + An array sized for the number of outputs. + Unblinded outputs should have a 'null' placeholder element. + The commitments as retrieved from `get_commitments()`, with the addition of: + 'blinding_key', - the output's public blinding key + (as retrieved from `get_blinding_key()`) + + change : [dict] + An array sized for the number of outputs. + Outputs which are not to this wallet should have a 'null' placeholder element. + The output scripts for the elements with data will be verified by Jade. + Unless the element also contains 'is_change': False, these outputs will automatically + be approved and not be verified by the user. + Populated elements should contain sufficient data to generate the wallet address. + See `get_receive_address()` + + use_ae_signatures : bool, optional + Whether to use the anti-exfil protocol to generate the signatures. + Defaults to False. + + asset_info : [dict], optional + Any asset-registry data relevant to the assets being transacted, such that Jade can + display a meaningful name, issuer, ticker etc. rather than just asset-id. + At the very least must contain 'asset_id', 'contract' and 'issuance_prevout' items, + exactly as in the registry data. NOTE: asset_info for the network policy-asset is + not required. + Defaults to None. + + additional_info: dict, optional + Extra data about the transaction. Only required for advanced transactions, eg. swaps. + Not needed for vanilla send-payment/redeposit etc: + tx_type, str: 'swap' indicates the tx represents an asset-swap proposal or transaction. + wallet_input_summary, dict: a list of entries containing 'asset_id' (32-bytes) and + 'satoshi' (int) showing net movement of assets out of the wallet (ie. sum of wallet + inputs per asset, minus any change outputs). + wallet_output_summary, dict: a list of entries containing 'asset_id' (32-bytes) and + 'satoshi' (int) showing net movement of assets into the wallet (ie. sum of wallet + outputs per asset, excluding any change outputs). + + Returns + ------- + 1. if use_ae_signatures is False + [bytes] + An array of signatures corresponding to the array of inputs passed. + The signatures are in DER format with the sighash appended. + 'None' placeholder elements are used for inputs not requiring a signature. + + 2. if use_ae_signatures is True + [(32-bytes, bytes)] + An array of pairs of signer-commitments and signatures corresponding to the inputs. + The signatures are in DER format with the sighash appended. + (None, None) placeholder elements are used for inputs not requiring a signature. + """ # 1st message contains txn and number of inputs we are going to send. # Reply ok if that corresponds to the expected number of inputs (n). base_id = 100 * random.randint(1000, 9999) @@ -478,6 +1676,8 @@ def sign_liquid_tx( "trusted_commitments": commitments, "use_ae_signatures": use_ae_signatures, "change": change, + "asset_info": asset_info, + "additional_info": additional_info, } reply = self._jadeRpc("sign_liquid_tx", params, str(base_id)) @@ -486,8 +1686,63 @@ def sign_liquid_tx( # Send inputs and receive signatures return self._send_tx_inputs(base_id, inputs, use_ae_signatures) - # Sign a txn def sign_tx(self, network, txn, inputs, change, use_ae_signatures=False): + """ + RPC call to sign a btc transaction. + + Parameters + ---------- + network : str + Network to which the txn should apply - eg. 'mainnet', 'testnet', etc. + + txn : bytes + The transaction to sign + + inputs : [dict] + The tx inputs. Should contain keys: + One of these is required: + input_tx, bytes - The prior transaction which created the utxo of this input + satoshi, int - The satoshi amount of this input - can be used in place of + 'input_tx' for a tx with a single segwit input + + These are only required if signing this input: + is_witness, bool - whether this is a segwit input + script, bytes- the redeem script + path, [int] - the bip32 path to sign with + + This is optional if signing this input: + sighash, int - The sighash to use, defaults to 0x01 (SIGHASH_ALL) + + These are only required for Anti-Exfil signatures: + ae_host_commitment, 32-bytes - The host-commitment for Anti-Exfil signatures + ae_host_entropy, 32-bytes - The host-entropy for Anti-Exfil signatures + + change : [dict] + An array sized for the number of outputs. + Outputs which are not to this wallet should have a 'null' placeholder element. + The output scripts for the elements with data will be verified by Jade. + Unless the element also contains 'is_change': False, these outputs will automatically + be approved and not be verified by the user. + Populated elements should contain sufficient data to generate the wallet address. + See `get_receive_address()` + + use_ae_signatures : bool + Whether to use the anti-exfil protocol to generate the signatures + + Returns + ------- + 1. if use_ae_signatures is False + [bytes] + An array of signatures corresponding to the array of inputs passed. + The signatures are in DER format with the sighash appended. + 'None' placeholder elements are used for inputs not requiring a signature. + + 2. if use_ae_signatures is True + [(32-bytes, bytes)] + An array of pairs of signer-commitments and signatures corresponding to the inputs. + The signatures are in DER format with the sighash appended. + (None, None) placeholder elements are used for inputs not requiring a signature. + """ # 1st message contains txn and number of inputs we are going to send. # Reply ok if that corresponds to the expected number of inputs (n). base_id = 100 * random.randint(1000, 9999) @@ -505,27 +1760,74 @@ def sign_tx(self, network, txn, inputs, change, use_ae_signatures=False): # Send inputs and receive signatures return self._send_tx_inputs(base_id, inputs, use_ae_signatures) + def sign_psbt(self, network, psbt): + """ + RPC call to sign a passed psbt as required + + Parameters + ---------- + network : str + Network to which the txn should apply - eg. 'mainnet', 'testnet', etc. + + psbt : bytes + The psbt formatted as bytes + + Returns + ------- + bytes + The psbt, updated with any signatures required from the hw signer + """ + # Send PSBT message + params = {"network": network, "psbt": psbt} + msgid = str(random.randint(100000, 999999)) + request = self.jade.build_request(msgid, "sign_psbt", params) + self.jade.write_request(request) + + # Read replies until we have them all, collate data and return. + # NOTE: we send 'get_extended_data' messages to request more 'chunks' of the reply data. + psbt_out = bytearray() + while True: + reply = self.jade.read_response() + self.jade.validate_reply(request, reply) + psbt_out.extend(self._get_result_or_raise_error(reply)) + + if "seqnum" not in reply or reply["seqnum"] == reply["seqlen"]: + break + + newid = str(random.randint(100000, 999999)) + params = { + "origid": msgid, + "orig": "sign_psbt", + "seqnum": reply["seqnum"] + 1, + "seqlen": reply["seqlen"], + } + request = self.jade.build_request(newid, "get_extended_data", params) + self.jade.write_request(request) + + return psbt_out + -# -# Mid-level interface to Jade -# Wraps either a serial or a ble connection -# Calls to send and receive bytes and cbor messages over the interface. -# -# Either: -# a) use wrapped with JadeAPI -# (recommended) -# or: -# b) use with JadeInterface.create_[serial|ble]() as jade: -# ... -# or: -# c) use JadeInterface.create_[serial|ble], then call connect() before -# using, and disconnect() when finished -# (caveat cranium) -# or: -# d) use ctor to wrap existing low-level implementation instance -# (caveat cranium) -# class JadeInterface: + """ + Mid-level interface to Jade + Wraps either a serial or a ble connection + Calls to send and receive bytes and cbor messages over the interface. + + Either: + a) use wrapped with JadeAPI + (recommended) + or: + b) use with JadeInterface.create_[serial|ble]() as jade: + ... + or: + c) use JadeInterface.create_[serial|ble], then call connect() before + using, and disconnect() when finished + (caveat cranium) + or: + d) use ctor to wrap existing low-level implementation instance + (caveat cranium) + """ + def __init__(self, impl): assert impl is not None self.impl = impl @@ -536,45 +1838,124 @@ def __enter__(self): def __exit__(self, exc_type, exc, tb): if exc_type: - logger.error("Exception causing JadeInterface context exit.") - logger.error(exc_type) - logger.error(exc) + logger.info("Exception causing JadeInterface context exit.") + logger.info(exc_type) + logger.info(exc) traceback.print_tb(tb) self.disconnect(exc_type is not None) @staticmethod def create_serial(device=None, baud=None, timeout=None): + """ + Create a JadeInterface object using the serial interface described. + + Parameters + ---------- + device : str, optional + The device identifier for the serial device. + Underlying implementation will default (to /dev/ttyUSB0) + + baud : int, optional + The communication baud rate. + Underlying implementation will default (to 115200) + + timeout : int, optional + The serial read timeout when awaiting messages. + Underlying implementation will default (to 120s) + + Returns + ------- + JadeInterface + Inerface object configured to use given serial parameters. + NOTE: the instance has not yet tried to contact the hw + - caller must call 'connect()' before trying to use the Jade. + """ if device and JadeTCPImpl.isSupportedDevice(device): - impl = JadeTCPImpl(device) + impl = JadeTCPImpl(device, timeout or DEFAULT_SERIAL_TIMEOUT) else: impl = JadeSerialImpl( - device or DEFAULT_SERIAL_DEVICE, - baud or DEFAULT_BAUD_RATE, - timeout or DEFAULT_SERIAL_TIMEOUT, + device, baud or DEFAULT_BAUD_RATE, timeout or DEFAULT_SERIAL_TIMEOUT ) return JadeInterface(impl) - # @staticmethod - # def create_ble(device_name=None, serial_number=None, scan_timeout=None, loop=None): - # impl = JadeBleImpl( - # device_name or DEFAULT_BLE_DEVICE_NAME, - # serial_number or DEFAULT_BLE_SERIAL_NUMBER, - # scan_timeout or DEFAULT_BLE_SCAN_TIMEOUT, - # loop=loop, - # ) - # return JadeInterface(impl) + @staticmethod + def create_ble(device_name=None, serial_number=None, scan_timeout=None, loop=None): + """ + Create a JadeInterface object using the BLE interface described. + NOTE: raises JadeError if BLE dependencies not installed. + + Parameters + ---------- + device_name : str, optional + The device name of the desired BLE device. + Underlying implementation will default (to 'Jade') + + serial_number : int, optional + The serial number of the desired BLE device + - used to disambiguate multiple beacons with the same 'device name' + Underlying implementation will connect to the first beacon it scans + with the matching 'device name'. + + scan_timeout : int, optional + The timeout when scanning for devices which match the device name/serial number. + Underlying implementation will default (to 60s) + + loop : optional + The asynchio event loop to use, if required. + Underlying implementation will default (to asyncio.get_event_loop()) + + Returns + ------- + JadeInterface + Inerface object configured to use given BLE parameters. + NOTE: the instance has not yet tried to contact the hw + - caller must call 'connect()' before trying to use the Jade. + + Raises + ------ + JadeError if BLE backend not available (ie. BLE dependencies not installed) + """ + this_module = sys.modules[__name__] + if not hasattr(this_module, "JadeBleImpl"): + raise JadeError(1, "BLE support not installed", None) + + impl = JadeBleImpl( + device_name or DEFAULT_BLE_DEVICE_NAME, + serial_number or DEFAULT_BLE_SERIAL_NUMBER, + scan_timeout or DEFAULT_BLE_SCAN_TIMEOUT, + loop=loop, + ) + return JadeInterface(impl) def connect(self): + """ + Try to connect the underlying transport interface (eg. serial, ble, etc.) + Raises an exception on failure. + """ self.impl.connect() def disconnect(self, drain=False): + """ + Disconnect the underlying transport (eg. serial, ble, etc.) + + Parameters + ---------- + drain : bool, optional + When true log any/all remaining messages/data, otherwise silently discard. + NOTE: can prevent disconnection if data is arriving constantly. + Defaults to False. + """ if drain: self.drain() self.impl.disconnect() def drain(self): - logger.warn("Draining interface...") + """ + Log any/all outstanding messages/data. + NOTE: can run indefinitely if data is arriving constantly. + """ + logger.warning("Draining interface...") drained = bytearray() finished = False @@ -585,20 +1966,40 @@ def drain(self): if finished or byte_ == b"\n" or len(drained) > 256: try: - device_logger.warn(drained.decode("utf-8")) + device_logger.warning(drained.decode("utf-8")) except Exception as e: # Dump the bytes raw and as hex if decoding as utf-8 failed - device_logger.warn("Raw:") - device_logger.warn(drained) - device_logger.warn("----") - device_logger.warn("Hex dump:") - device_logger.warn(drained.hex()) + device_logger.warning("Raw:") + device_logger.warning(drained) + device_logger.warning("----") + device_logger.warning("Hex dump:") + device_logger.warning(drained.hex()) # Clear and loop to continue collecting drained.clear() @staticmethod def build_request(input_id, method, params=None): + """ + Build a request dict from passed parameters + + Parameters + ---------- + input_id : str + The id of the request message to construct + + method : str + rpc method to invoke + + params : dict, optional + any parameters to pass to the rpc method + Defaults to None. + + Returns + ------- + dict + The request object as a dict + """ request = {"method": method, "id": input_id} if params is not None: request["params"] = params @@ -606,6 +2007,19 @@ def build_request(input_id, method, params=None): @staticmethod def serialise_cbor_request(request): + """ + Method to format a request dict as a cbor message + + Parameters + ---------- + request : dict + The request dict + + Returns + ------- + bytes + The request formatted as cbor message bytes + """ dump = cbor.dumps(request) len_dump = len(dump) if "method" in request and "ota_data" in request["method"]: @@ -620,56 +2034,118 @@ def serialise_cbor_request(request): return dump def write(self, bytes_): + """ + Write bytes over the underlying interface + + Parameters + ---------- + bytes_ : bytes + The bytes to write + + Returns + ------- + int + The number of bytes written + """ logger.debug("Sending: {} bytes".format(len(bytes_))) wrote = self.impl.write(bytes_) logger.debug("Sent: {} bytes".format(len(bytes_))) return wrote def write_request(self, request): + """ + Write a request dict over the underlying interface, formatted as cbor. + + Parameters + ---------- + request : dict + The request dict to write + """ msg = self.serialise_cbor_request(request) written = 0 while written < len(msg): written += self.write(msg[written:]) def read(self, n): + """ + Try to read bytes from the underlying interface. + + Returns + ------- + bytes + The bytes received + """ logger.debug("Reading {} bytes...".format(n)) bytes_ = self.impl.read(n) logger.debug("Received: {} bytes".format(len(bytes_))) return bytes_ def read_cbor_message(self): + """ + Try to read a single cbor (response) message from the underlying interface. + Respects the any read timeout. + If any 'log' messages are received, logs them locally at the nearest corresponding level + and awaits the next message. Returns when it receives what appears to be a reply message. + + Returns + ------- + dict + The message received, as a dict + """ while True: # 'self' is sufficiently 'file-like' to act as a load source. # Throws EOFError on end of stream/timeout/lost-connection etc. message = cbor.load(self) - # A message response (to a prior request) - if "id" in message: - logger.info("Received msg: {}".format(_hexlify(message))) - return message - - # A log message - handle as normal - if "log" in message: - response = message["log"].decode("utf-8") - log_methods = { - "E": device_logger.error, - "W": device_logger.warn, - "I": device_logger.info, - "D": device_logger.debug, - "V": device_logger.debug, - } - log_method = device_logger.error - if len(response) > 1 and response[1] == " ": - lvl = response[0] - log_method = log_methods.get(lvl, device_logger.error) - - log_method(">> {}".format(response)) - else: - # Unknown/unhandled/unexpected message - logger.error("Unhandled message received") - device_logger.error(message) + if isinstance(message, collections.abc.Mapping): + # A message response (to a prior request) + if "id" in message: + logger.info("Received msg: {}".format(_hexlify(message))) + return message + + # A log message - handle as normal + if "log" in message: + response = message["log"] + log_method = device_logger.error + try: + response = message["log"].decode("utf-8") + log_methods = { + "E": device_logger.error, + "W": device_logger.warning, + "I": device_logger.info, + "D": device_logger.debug, + "V": device_logger.debug, + } + if len(response) > 1 and response[1] == " ": + lvl = response[0] + log_method = log_methods.get(lvl, device_logger.error) + except Exception as e: + logger.error("Error processing log message: {}".format(e)) + log_method(">> {}".format(response)) + continue + + # Unknown/unhandled/unexpected message + logger.error("Unhandled message received") + device_logger.error(message) def read_response(self, long_timeout=False): + """ + Try to read a single cbor (response) message from the underlying interface. + If any 'log' messages are received, logs them locally at the nearest corresponding level + and awaits the next message. Returns when it receives what appears to be a reply message. + If `long_timeout` is false, any read-timeout is respected. If True, the call will block + indefinitely awaiting a response message. + + Parameters + ---------- + long_timeout : bool + Whether to wait indefinitely for the next (response) message. + + Returns + ------- + dict + The message received, as a dict + """ while True: try: return self.read_cbor_message() @@ -679,11 +2155,32 @@ def read_response(self, long_timeout=False): @staticmethod def validate_reply(request, reply): + """ + Helper to minimally validate a reply message, in the context of a request. + Asserts if the reply does contain the expected minimal fields. + """ assert isinstance(reply, dict) and "id" in reply assert ("result" in reply) != ("error" in reply) assert reply["id"] == request["id"] or reply["id"] == "00" and "error" in reply def make_rpc_call(self, request, long_timeout=False): + """ + Method to send a request over the underlying interface, and await a response. + The request is minimally validated before it is sent, and the response is simialrly + validated before being returned. + Any read-timeout is respected unless 'long_timeout' is passed, in which case the call + blocks indefinitely awaiting a response. + + Parameters + ---------- + long_timeout : bool + Whether to wait indefinitely for the response. + + Returns + ------- + dict + The (minimally validated) response message received, as a dict + """ # Write outgoing request message assert isinstance(request, dict) assert "id" in request and len(request["id"]) > 0 diff --git a/src/cryptoadvance/specter/devices/hwi/jadepy/jade_ble.py b/src/cryptoadvance/specter/devices/hwi/jadepy/jade_ble.py new file mode 100644 index 0000000000..454cd0ff24 --- /dev/null +++ b/src/cryptoadvance/specter/devices/hwi/jadepy/jade_ble.py @@ -0,0 +1,244 @@ +import logging +import asyncio +import aioitertools +import collections +import subprocess +import platform +import bleak + +from .jade_error import JadeError + +logger = logging.getLogger(__name__) + + +# +# Low-level BLE backend interface to Jade +# Calls to send and receive bytes over the interface. +# Intended for use via JadeInterface wrapper. +# +# Either: +# a) use via JadeInterface.create_ble() (see JadeInterface) +# (recommended) +# or: +# b) use JadeBleImpl() directly, and call connect() before +# using, and disconnect() when finished, +# (caveat cranium) +# +class JadeBleImpl: + IO_SERVICE_UUID = "6e400001-b5a3-f393-e0a9-e50e24dcca9e" + IO_TX_CHAR_UUID = "6e400002-b5a3-f393-e0a9-e50e24dcca9e" + IO_RX_CHAR_UUID = "6e400003-b5a3-f393-e0a9-e50e24dcca9e" + BLE_MAX_WRITE_SIZE = 517 - 8 + + def __init__(self, device_name, serial_number, scan_timeout, loop=None): + self.device_name = device_name + self.serial_number = serial_number + self.scan_timeout = max(1, scan_timeout) + self.inputstream = None + self.write_task = None + self.client = None + self.rx_char_handle = None + + if not loop: + loop = asyncio.get_event_loop() + self.loop = loop + + # Helper to await async coroutines + def _run(self, coro): + assert coro and self.loop and not self.loop.is_closed() + return self.loop.run_until_complete(coro) + + async def _connect_impl(self): + assert self.client is None + + # Input received, buffered awaiting external read + inbufs = collections.deque() + + # Async stream of those items for reading + async def _input_stream(): + # Poll for new input all the time client exists + while self.client is not None: + while inbufs: + buf = inbufs.popleft() + for b in buf: + yield b + + # No data, yield to event loop awaiting arrival of more data + await asyncio.sleep(0.01) + + # Stream drained and client connection no longer exists + self.inputstream = None + + self.inputstream = _input_stream() + + # Scan for expected ble device + # Match device-name only if no serial number provided + device_mac = None + while not device_mac and self.scan_timeout > 0: + logger.info("Scanning, timeout = {}s".format(self.scan_timeout)) + scan_time = min(2, self.scan_timeout) + self.scan_timeout -= scan_time + + devices = await bleak.discover(scan_time) + for dev in devices: + logger.debug("Seen: {}".format(dev.name)) + if ( + dev.name + and dev.name.startswith(self.device_name) + and ( + self.serial_number is None + or dev.name.endswith(self.serial_number) + ) + ): + # Map pretty name to mac-type address + device_mac = dev.address + full_name = dev.name + + if not device_mac: + raise JadeError( + 1, + "Unable to locate BLE device", + "Device name: {}, Serial number: {}".format( + self.device_name, self.serial_number or "" + ), + ) + + # Remove previous bt/ble pairing data for this device + if platform.system() == "Linux": + command = "bt-device --remove '{}'".format(device_mac) + process = subprocess.run(command, shell=True, stdout=subprocess.DEVNULL) + + # Connect - seems pretty flaky so allow retries + connected = False + attempts_remaining = 3 + while not connected: + try: + attempts_remaining -= 1 + client = bleak.BleakClient(device_mac) + logger.info("Connecting to: {} ({})".format(full_name, device_mac)) + await client.connect() + connected = client.is_connected + logger.info("Connected: {}".format(connected)) + except Exception as e: + logger.warning("BLE connection exception: '{}'".format(e)) + if not attempts_remaining: + logger.warning("Exhausted retries - BLE connection failed") + raise + + # Peruse services and characteristics + # Get the 'handle' of the receiving charactersitic + for service in client.services: + for char in service.characteristics: + if char.uuid == JadeBleImpl.IO_RX_CHAR_UUID: + logger.debug( + "Found RX characterisitic - handle: ".format(char.handle) + ) + self.rx_char_handle = char.handle + + if "read" in char.properties: + await client.read_gatt_char(char.uuid) + + for descriptor in char.descriptors: + await client.read_gatt_descriptor(descriptor.handle) + + # Attach handler to be notified of new data on the receiving characteristic + def _notification_handler(char_handle, data): + assert char_handle == self.rx_char_handle + inbufs.append(data) + + assert self.rx_char_handle + await client.start_notify(self.rx_char_handle, _notification_handler) + + # Attach handler called when disconnected + def _disconnection_handler(client): + + # Set the client to None - that will cause the receive + # generator to terminate and not wait forever for data. + assert client == self.client + self.client = None + + # Also cancel any running task trying to write data, + # as otherwise that hangs forever too ... + if self.write_task: + self.write_task.cancel() + self.write_task = None + + client.set_disconnected_callback(_disconnection_handler) + + # Done + self.client = client + + def connect(self): + return self._run(self._connect_impl()) + + async def _disconnect_impl(self): + try: + if self.client is not None and self.client.is_connected: + # Stop listening for incoming data + if self.rx_char_handle: + await self.client.stop_notify(self.rx_char_handle) + + # Disconnect underlying client - this should trigger the _disconnection_handler() + # above to run before this returns from the 'await' + await self.client.disconnect() + except Exception as err: + # Sometimes get an exception when testing connection + # if the client has already internally disconnected ... + logger.warning("Exception when disconnecting ble: {}".format(err)) + + # Set the client to None in any case - that will cause the receive + # generator to terminate and not wait forever for data. + self.rx_char_handle = None + self.client = None + + def disconnect(self): + return self._run(self._disconnect_impl()) + + async def _write_impl(self, bytes_): + assert self.client is not None + assert self.write_task is None + + towrite = len(bytes_) + written = 0 + + async def _write(): + if self.client is not None: + nonlocal written + + # Write out in small chunks + while written < towrite: + remaining = towrite - written + length = min(remaining, JadeBleImpl.BLE_MAX_WRITE_SIZE) + ulimit = written + length + await self.client.write_gatt_char( + JadeBleImpl.IO_TX_CHAR_UUID, + bytearray(bytes_[written:ulimit]), + response=True, + ) + + written = ulimit + + # Hold on to the write task in case we need to cancel it + # whie it is running (eg. unexpected disconnection) + self.write_task = asyncio.create_task(_write()) + try: + await self.write_task + except asyncio.CancelledError: + logger.warning( + "write() task cancelled having written " + "{} of {} bytes".format(written, towrite) + ) + finally: + self.write_task = None + + return written + + def write(self, bytes_): + return self._run(self._write_impl(bytes_)) + + async def _read_impl(self, n): + assert self.inputstream is not None + return bytes([b async for b in aioitertools.islice(self.inputstream, n)]) + + def read(self, n): + return self._run(self._read_impl(n)) diff --git a/src/cryptoadvance/specter/devices/hwi/jadepy/jade_serial.py b/src/cryptoadvance/specter/devices/hwi/jadepy/jade_serial.py index e82e4753ea..075d49cdd2 100644 --- a/src/cryptoadvance/specter/devices/hwi/jadepy/jade_serial.py +++ b/src/cryptoadvance/specter/devices/hwi/jadepy/jade_serial.py @@ -1,8 +1,9 @@ import serial import logging +from serial.tools import list_ports -logger = logging.getLogger("jade.serial") +logger = logging.getLogger(__name__) # @@ -19,8 +20,28 @@ # (caveat cranium) # class JadeSerialImpl: + # Used when searching for devices that might be a Jade/compatible hw + JADE_DEVICE_IDS = [ + (0x10C4, 0xEA60), + (0x1A86, 0x55D4), + (0x0403, 0x6001), + (0x1A86, 0x7523), + ] + + @classmethod + def _get_first_compatible_device(cls): + jades = [] + for devinfo in list_ports.comports(): + if (devinfo.vid, devinfo.pid) in cls.JADE_DEVICE_IDS: + jades.append(devinfo.device) + + if len(jades) > 1: + logger.warning(f"Multiple potential jade devices detected: {jades}") + + return jades[0] if jades else None + def __init__(self, device, baud, timeout): - self.device = device + self.device = device or self._get_first_compatible_device() self.baud = baud self.timeout = timeout self.ser = None @@ -33,12 +54,24 @@ def connect(self): self.device, self.baud, timeout=self.timeout, write_timeout=self.timeout ) assert self.ser is not None - self.ser.__enter__() + + if not self.ser.is_open: + self.ser.open() + + # Ensure RTS and DTR are not set (as this can cause the hw to reboot) + self.ser.setRTS(False) + self.ser.setDTR(False) + logger.info("Connected") def disconnect(self): assert self.ser is not None - self.ser.__exit__() + + # Ensure RTS and DTR are not set (as this can cause the hw to reboot) + # and then close the connection + self.ser.setRTS(False) + self.ser.setDTR(False) + self.ser.close() # Reset state self.ser = None diff --git a/src/cryptoadvance/specter/devices/hwi/jadepy/jade_tcp.py b/src/cryptoadvance/specter/devices/hwi/jadepy/jade_tcp.py index 0849182cf0..712201fcbd 100644 --- a/src/cryptoadvance/specter/devices/hwi/jadepy/jade_tcp.py +++ b/src/cryptoadvance/specter/devices/hwi/jadepy/jade_tcp.py @@ -2,7 +2,7 @@ import logging -logger = logging.getLogger("jade.tcp") +logger = logging.getLogger(__name__) # @@ -25,9 +25,10 @@ class JadeTCPImpl: def isSupportedDevice(cls, device): return device is not None and device.startswith(cls.PROTOCOL_PREFIX) - def __init__(self, device): + def __init__(self, device, timeout): assert self.isSupportedDevice(device) self.device = device + self.timeout = timeout self.tcp_sock = None def connect(self): @@ -36,6 +37,7 @@ def connect(self): logger.info("Connecting to {}".format(self.device)) self.tcp_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + self.tcp_sock.settimeout(self.timeout) url = self.device[len(self.PROTOCOL_PREFIX) :].split(":") self.tcp_sock.connect((url[0], int(url[1]))) @@ -57,4 +59,7 @@ def write(self, bytes_): def read(self, n): assert self.tcp_sock is not None - return self.tcp_sock.recv(n) + buf = self.tcp_sock.recv(n) + while len(buf) < n: + buf += self.tcp_sock.recv(n - len(buf)) + return buf diff --git a/src/cryptoadvance/specter/devices/jade.py b/src/cryptoadvance/specter/devices/jade.py index e9d0a7afd7..e2b697e382 100644 --- a/src/cryptoadvance/specter/devices/jade.py +++ b/src/cryptoadvance/specter/devices/jade.py @@ -18,6 +18,7 @@ class Jade(HWIDevice): supports_qr_message_signing = True supports_hwi_toggle_passphrase = False supports_hwi_multisig_display_address = True + supports_multisig_registration = True liquid_support = True exportable_to_wallet = True wallet_export_type = "qr" diff --git a/src/cryptoadvance/specter/helpers.py b/src/cryptoadvance/specter/helpers.py index dbdc6363d9..b5362fdf2c 100644 --- a/src/cryptoadvance/specter/helpers.py +++ b/src/cryptoadvance/specter/helpers.py @@ -112,10 +112,9 @@ def to_ascii20(name: str) -> str: return "".join([c for c in name if ord(c) < 127])[:20] -# TODO: Rename this function to sth. like create_unique_id -def alias(name): +def create_unique_id(name): """ - Create a filesystem-friendly alias from a string. + Create a filesystem-friendly unique ID from a string. Leading and trailing whitespaces are removed. Replaces space(s) and hyphen(s) with one underscore. Keeps only alphanumeric chars and returns in lowercase. @@ -128,12 +127,12 @@ def alias(name): def fullpath(data_folder, name): """Quick way to get a fullpath which usually""" - return os.path.join(data_folder, f"{alias(name)}.json") + return os.path.join(data_folder, f"{create_unique_id(name)}.json") def calc_fullpath(data_folder, name): """Get a fullpath for a Businessobject with a name quickly""" - return os.path.join(data_folder, f"{alias(name)}.json") + return os.path.join(data_folder, f"{create_unique_id(name)}.json") def deep_update(d, u): diff --git a/src/cryptoadvance/specter/hwi_rpc.py b/src/cryptoadvance/specter/hwi_rpc.py index b4a097e71b..c6ae6e0bdc 100644 --- a/src/cryptoadvance/specter/hwi_rpc.py +++ b/src/cryptoadvance/specter/hwi_rpc.py @@ -3,13 +3,13 @@ from contextlib import contextmanager from typing import Callable -import bitbox02 import hwilib.commands as hwi_commands from embit import bip32 from embit.liquid import networks from flask import current_app as app from hwilib.common import Chain from hwilib.devices.bitbox02 import Bitbox02Client +from hwilib.devices.bitbox02_lib.util import BitBoxAppNoiseConfig from hwilib.devices.trezorlib.transport import get_transport from hwilib.psbt import PSBT from usb1 import USBError @@ -39,7 +39,7 @@ hwilock = threading.Lock() -class BitBox02NoiseConfig(bitbox02.util.BitBoxAppNoiseConfig): +class BitBox02NoiseConfig(BitBoxAppNoiseConfig): def show_pairing(self, code: str, device_response: Callable[[], bool]) -> bool: config = hwi_get_config(app.specter) config["bitbox02_pairing_code"] = code @@ -66,7 +66,7 @@ class HWIBridge(JSONRPC): All methods of this class are callable over JSON-RPC, except _underscored. """ - def __init__(self, enforce_hwi_initialisation=False): + def __init__(self, skip_hwi_initialisation=False): self.exposed_rpc = { "enumerate": self.enumerate, "detect_device": self.detect_device, @@ -79,16 +79,20 @@ def __init__(self, enforce_hwi_initialisation=False): "sign_tx": self.sign_tx, "sign_message": self.sign_message, "extract_master_blinding_key": self.extract_master_blinding_key, + "register_multisig": self.register_multisig, # currently only Jade "bitbox02_pairing": self.bitbox02_pairing, } - if enforce_hwi_initialisation: - # Running enumerate after beginning an interaction with a specific device - # crashes python or make HWI misbehave. For now we just get all connected - # devices once per session and save them. - logger.info("Initializing HWI...") # to explain user why it takes so long - self.enumerate() - logger.info("Finished initializing HWI!") - self.devices = [] + if skip_hwi_initialisation: + self.is_startup = False + self.devices = [] + return + # Running enumerate after beginning an interaction with a specific device + # crashes python or make HWI misbehave. For now we just get all connected + # devices once per session and save them. + logger.info("Initializing HWI...") + self.is_startup = True # to explain user why it takes so long + self.enumerate() + logger.info("Finished initializing HWI!") @locked(hwilock) def enumerate(self, passphrase="", chain=""): @@ -97,37 +101,21 @@ def enumerate(self, passphrase="", chain=""): Standard HWI enumerate() command + Specter. """ devices = [] - # going through all device classes + # Call device-specific enumerate (can come from hwi lib or from the Specter code base) for each Specter device class for devcls in hwi_classes: try: - # calling device-specific enumerate - if passphrase: - devs = devcls.enumerate(passphrase) - # not sure if it will handle passphrase correctly - # so remove it if None + # Special handling of the Jade to unsure it is not prompting to unlock the device on startup + if devcls.__name__ == "Jade": + skip_unlocking = self.is_startup + client_chain = Chain.argparse(chain) # This returns an enum member + devs = devcls.enumerate( + skip_unlocking=skip_unlocking, chain=client_chain + ) else: - devs = devcls.enumerate() - # extracting fingerprint info - for dev in devs: - # we can't get fingerprint if device is locked - if "needs_pin_sent" in dev and dev["needs_pin_sent"]: - continue - # we can't get fingerprint if passphrase is not provided - if ( - "needs_passphrase_sent" in dev - and dev["needs_passphrase_sent"] - and not passphrase - ): - continue - client = None - try: - client = devcls.get_client(dev["path"], passphrase) - if isinstance(client, Bitbox02Client): - client.set_noise_config(BitBox02NoiseConfig()) - dev["fingerprint"] = client.get_master_fingerprint().hex() - finally: - if client is not None: - client.close() + if passphrase: + devs = devcls.enumerate(passphrase) + else: + devs = devcls.enumerate() devices += devs except USBError as e: logger.warning( @@ -138,6 +126,7 @@ def enumerate(self, passphrase="", chain=""): ) self.devices = devices + self.is_startup = False return self.devices def detect_device( @@ -316,6 +305,34 @@ def display_address( else: raise Exception("Failed to validate address on device: Unknown Error") + @locked(hwilock) + def register_multisig( + self, + device_type=None, + path=None, + passphrase="", + fingerprint=None, + descriptor="", + chain="", + ): + if descriptor == "": + raise Exception("Descriptor must not be empty") + + with self._get_client( + device_type=device_type, + fingerprint=fingerprint, + path=path, + passphrase=passphrase, + chain=chain, + ) as client: + try: + return client.register_multisig(descriptor) + except Exception as e: + logger.exception(e) + raise Exception( + f"Failed to register multisig on the device. Error: {e}" + ) + @locked(hwilock) def sign_tx( self, @@ -426,22 +443,24 @@ def _get_client( ) devcls = get_device_class(device["type"]) if devcls: - client = devcls.get_client(device["path"], passphrase) + # Jade needs the chain/network already here for the the auth_call + if devcls.__name__ == "Jade": + client_chain = Chain.argparse(chain) + client = devcls.get_client( + path=device["path"], + password=passphrase, + expert=False, + chain=client_chain, + ) + else: + client = devcls.get_client(device["path"], passphrase) if not client: raise Exception( "The device was identified but could not be reached. Please check it is properly connected and try again" ) try: - if is_liquid(chain): - client.chain = Chain.TEST if is_testnet(chain) else Chain.MAIN - else: + if type(client) is not JadeClient: client.chain = Chain.argparse(chain) - # hack for Jade - if type(client) is JadeClient: - if is_liquid(chain): - client.set_liquid_network(chain) - elif chain == "signet": - client.chain = Chain.TEST yield client finally: client.close() diff --git a/src/cryptoadvance/specter/managers/device_manager.py b/src/cryptoadvance/specter/managers/device_manager.py index 16b3b303fb..6b760c8bd3 100644 --- a/src/cryptoadvance/specter/managers/device_manager.py +++ b/src/cryptoadvance/specter/managers/device_manager.py @@ -6,7 +6,7 @@ from cryptoadvance.specter.devices.bitcoin_core import BitcoinCore, BitcoinCoreWatchOnly -from ..helpers import alias, load_jsons +from ..helpers import create_unique_id, load_jsons from ..rpc import get_default_datadir from ..devices import __all__ as all_device_classes @@ -59,11 +59,11 @@ def devices_names(self): return sorted(self.devices.keys()) def add_device(self, name, device_type, keys): - device_alias = alias(name) + device_alias = create_unique_id(name) fullpath = os.path.join(self.data_folder, "%s.json" % device_alias) i = 2 while os.path.isfile(fullpath): - device_alias = alias("%s %d" % (name, i)) + device_alias = create_unique_id("%s %d" % (name, i)) fullpath = os.path.join(self.data_folder, "%s.json" % device_alias) i += 1 # remove duplicated keys if any exist diff --git a/src/cryptoadvance/specter/managers/node_manager.py b/src/cryptoadvance/specter/managers/node_manager.py index e798ed55c5..9ea5c74e9a 100644 --- a/src/cryptoadvance/specter/managers/node_manager.py +++ b/src/cryptoadvance/specter/managers/node_manager.py @@ -7,7 +7,7 @@ from ..rpc import get_default_datadir, RPC_PORTS from ..specter_error import SpecterError, SpecterInternalException from ..persistence import PersistentObject, write_node, delete_file -from ..helpers import alias, calc_fullpath, load_jsons +from ..helpers import create_unique_id, calc_fullpath, load_jsons from ..node import Node, NonExistingNode from ..internal_node import InternalNode from ..services import callbacks @@ -193,11 +193,11 @@ def add_external_node( This should only be used for an external node. Use add_internal_node for internal node and if you have defined your own node type, use save_node directly to save the node (and create it yourself) """ - node_alias = alias(name) + node_alias = create_unique_id(name) fullpath = os.path.join(self.data_folder, "%s.json" % node_alias) i = 2 while os.path.isfile(fullpath): - node_alias = alias("%s %d" % (name, i)) + node_alias = create_unique_id("%s %d" % (name, i)) fullpath = os.path.join(self.data_folder, "%s.json" % node_alias) i += 1 @@ -239,11 +239,11 @@ def add_internal_node( This should only be used for internal nodes. Use add__External_node for external nodes and if you have defined your own node-type, use save_node directly. to save the node (and create it yourself) """ - node_alias = alias(name) + node_alias = create_unique_id(name) fullpath = os.path.join(self.data_folder, "%s.json" % node_alias) i = 2 while os.path.isfile(fullpath): - node_alias = alias("%s %d" % (name, i)) + node_alias = create_unique_id("%s %d" % (name, i)) fullpath = os.path.join(self.data_folder, "%s.json" % node_alias) i += 1 if not datadir: diff --git a/src/cryptoadvance/specter/managers/wallet_manager.py b/src/cryptoadvance/specter/managers/wallet_manager.py index 836592e4e6..9b07732b8a 100644 --- a/src/cryptoadvance/specter/managers/wallet_manager.py +++ b/src/cryptoadvance/specter/managers/wallet_manager.py @@ -11,7 +11,7 @@ from cryptoadvance.specter.rpc import BitcoinRPC from cryptoadvance.specter.key import Key -from ..helpers import add_dicts, alias, is_liquid, load_jsons +from ..helpers import add_dicts, create_unique_id, is_liquid, load_jsons from ..liquid.wallet import LWallet from ..persistence import delete_folder from ..rpc import RpcError, get_default_datadir, BrokenCoreConnectionException @@ -348,7 +348,7 @@ def create_wallet( except: walletsindir = [] self._check_duplicate_keys(keys) - wallet_alias = alias(name) + wallet_alias = create_unique_id(name) w = self.WalletClass.create( self.rpc, diff --git a/src/cryptoadvance/specter/process_controller/elementsd_controller.py b/src/cryptoadvance/specter/process_controller/elementsd_controller.py index c822893231..fe091954aa 100644 --- a/src/cryptoadvance/specter/process_controller/elementsd_controller.py +++ b/src/cryptoadvance/specter/process_controller/elementsd_controller.py @@ -70,17 +70,14 @@ def construct_node_cmd( btcd_cmd += " -validatepegin=0 " btcd_cmd += " -txindex=1 " btcd_cmd += " -initialfreecoins=2100000000000000 " - btcd_cmd += " -port={} -rpcport={} -rpcbind=0.0.0.0".format( - rpcconn.rpcport + 1, rpcconn.rpcport - ) + btcd_cmd += " -port={} -rpcport={}".format(rpcconn.rpcport + 1, rpcconn.rpcport) btcd_cmd += " -rpcuser={} -rpcpassword={} ".format( rpcconn.rpcuser, rpcconn.rpcpassword ) - btcd_cmd += " -rpcallowip=0.0.0.0/0 -rpcallowip=172.17.0.0/16 " if not run_docker: if not log_stdout: btcd_cmd += " -noprinttoconsole" - if datadir == None: + if datadir is None: datadir = tempfile.mkdtemp(prefix="bitcoind_datadir") btcd_cmd += ' -datadir="{}" '.format(datadir) print(f"extra_args={extra_args})") diff --git a/src/cryptoadvance/specter/process_controller/node_controller.py b/src/cryptoadvance/specter/process_controller/node_controller.py index 66f3daa33c..fa17db9d5b 100644 --- a/src/cryptoadvance/specter/process_controller/node_controller.py +++ b/src/cryptoadvance/specter/process_controller/node_controller.py @@ -1,11 +1,11 @@ """ Stuff to control a bitcoind-instance. """ + import atexit import json import logging import os import platform -import re import shutil import signal import subprocess @@ -48,7 +48,7 @@ def __init__( @property def ipaddress(self): - if self._ipaddress == None: + if self._ipaddress is None: raise Exception("ipadress is none") else: return self._ipaddress @@ -73,7 +73,7 @@ def get_rpc(self): return rpc def render_url(self, password_mask=False): - if password_mask == True: + if password_mask is True: password = "xxxxxx" else: password = self.rpcpassword @@ -226,7 +226,7 @@ def stop_node(self): def mine(self, address=None, block_count=1): """Does mining to the attached address with as many as block_count blocks""" - if address == None: + if address is None: if self.node_impl == "bitcoin": address = "mruae2834buqxk77oaVpephnA5ZAxNNJ1r" else: @@ -358,17 +358,14 @@ def construct_node_cmd( if network != "mainnet" and network != "main": btcd_cmd += " -{} ".format(network) btcd_cmd += " -fallbackfee=0.0002 " - btcd_cmd += " -port={} -rpcport={} -rpcbind=0.0.0.0 -rpcbind=0.0.0.0".format( - rpcconn.rpcport - 1, rpcconn.rpcport - ) + btcd_cmd += " -port={} -rpcport={}".format(rpcconn.rpcport - 1, rpcconn.rpcport) btcd_cmd += " -rpcuser={} -rpcpassword={} ".format( rpcconn.rpcuser, rpcconn.rpcpassword ) - btcd_cmd += " -rpcallowip=0.0.0.0/0 -rpcallowip=172.17.0.0/16 " if not run_docker: if not log_stdout: btcd_cmd += " -noprinttoconsole" - if datadir == None: + if datadir is None: datadir = tempfile.mkdtemp(prefix="bitcoind_datadir") btcd_cmd += ' -datadir="{}" '.format(datadir) if extra_args: @@ -412,7 +409,7 @@ def _start_node( log_stdout=False, extra_args=[], ): - if datadir == None: + if datadir is None: datadir = tempfile.mkdtemp( prefix=f"specter_{self.node_impl}_regtest_plain_datadir_" ) @@ -484,7 +481,7 @@ def cleanup_node(self, cleanup_hard=None, datadir=None): if not hasattr(self, "datadir"): self.datadir = None - if cleanup_hard == None: + if cleanup_hard is None: cleanup_hard = self.cleanup_hard if not hasattr(self, "node_proc"): logger.info("node process was not running") diff --git a/src/cryptoadvance/specter/rpc.py b/src/cryptoadvance/specter/rpc.py index abfda573ae..5272a6393f 100644 --- a/src/cryptoadvance/specter/rpc.py +++ b/src/cryptoadvance/specter/rpc.py @@ -505,7 +505,7 @@ def trace_call_after(cls, url, payload, timestamp): def __getattr__(self, method): def fn(*args, **kwargs): r = self.multi([(method, *args)], **kwargs)[0] - if r["error"] is not None: + if r.get("error") is not None: raise RpcError( f"Request error for method {method}{args}: {r['error']['message']}", r, diff --git a/src/cryptoadvance/specter/server.py b/src/cryptoadvance/specter/server.py index 0b9d495fc0..84dde61822 100644 --- a/src/cryptoadvance/specter/server.py +++ b/src/cryptoadvance/specter/server.py @@ -182,7 +182,7 @@ def service_manager_cleanup_on_exit(signum, frame): specter.initialize() # HWI - specter.hwi = HWIBridge(app.config["ENFORCE_HWI_INITIALISATION_AT_STARTUP"]) + specter.hwi = HWIBridge(app.config["SKIP_HWI_INITIALISATION_AT_STARTUP"]) login_manager = LoginManager() login_manager.session_protection = app.config.get("SESSION_PROTECTION", "strong") diff --git a/src/cryptoadvance/specter/server_endpoints/auth.py b/src/cryptoadvance/specter/server_endpoints/auth.py index 66e8cccd66..5ec57c2d94 100644 --- a/src/cryptoadvance/specter/server_endpoints/auth.py +++ b/src/cryptoadvance/specter/server_endpoints/auth.py @@ -10,7 +10,7 @@ from cryptoadvance.specter.specter import Specter -from ..helpers import alias, is_relative_url +from ..helpers import create_unique_id, is_relative_url from ..server_endpoints import flash from ..services import ExtensionException from ..user import User, hash_password, verify_password @@ -161,11 +161,11 @@ def register(): ) return redirect("register?otp={}".format(otp)) if app.specter.otp_manager.validate_new_user_otp(otp): - user_id = alias(username) + user_id = create_unique_id(username) i = 1 while app.specter.user_manager.get_user(user_id): i += 1 - user_id = "{}{}".format(alias(username), i) + user_id = "{}{}".format(create_unique_id(username), i) if app.specter.user_manager.get_user_by_username(username): flash( _("Username is already taken, please choose another one"), "error" diff --git a/src/cryptoadvance/specter/server_endpoints/settings.py b/src/cryptoadvance/specter/server_endpoints/settings.py index 15ee9e584e..14f9961c6f 100644 --- a/src/cryptoadvance/specter/server_endpoints/settings.py +++ b/src/cryptoadvance/specter/server_endpoints/settings.py @@ -751,6 +751,6 @@ def get_asset(asset): def backup_file(): return send_file( app.specter.specter_backup_file(), - attachment_filename="specter-backup.zip", + download_name="specter-backup.zip", as_attachment=True, ) diff --git a/src/cryptoadvance/specter/server_endpoints/setup.py b/src/cryptoadvance/specter/server_endpoints/setup.py index 99b9110690..e887cce26f 100644 --- a/src/cryptoadvance/specter/server_endpoints/setup.py +++ b/src/cryptoadvance/specter/server_endpoints/setup.py @@ -14,7 +14,7 @@ from flask_babel import lazy_gettext as _ from flask_login import login_required -from ..helpers import alias +from ..helpers import create_unique_id from ..util.bitcoind_setup_tasks import ( setup_bitcoind_directory_thread, setup_bitcoind_thread, @@ -28,6 +28,7 @@ # Setup endpoint blueprint setup_endpoint = Blueprint("setup_endpoint", __name__) + ######################### Setup pages ####################################### @setup_endpoint.route("/start/", methods=["GET"]) @login_required @@ -169,7 +170,8 @@ def setup_bitcoind_datadir(): else f"Specter {network.title()} {i}" ) node_default_datadir = os.path.join( - app.specter.node_manager.data_folder, f"{alias(node_name)}/.bitcoin-{network}" + app.specter.node_manager.data_folder, + f"{create_unique_id(node_name)}/.bitcoin-{network}", ) user_selected_datadir = request.form.get( "bitcoin_core_datadir", node_default_datadir diff --git a/src/cryptoadvance/specter/server_endpoints/wallets/wallets.py b/src/cryptoadvance/specter/server_endpoints/wallets/wallets.py index 654f6cea60..e39e79a3ea 100644 --- a/src/cryptoadvance/specter/server_endpoints/wallets/wallets.py +++ b/src/cryptoadvance/specter/server_endpoints/wallets/wallets.py @@ -11,7 +11,12 @@ from flask_login import login_required from ...commands.psbt_creator import PsbtCreator -from ...helpers import bcur2base64, get_devices_with_keys_by_type, get_txid, alias +from ...helpers import ( + bcur2base64, + get_devices_with_keys_by_type, + get_txid, + create_unique_id, +) from ...key import Key from ...managers.wallet_manager import purposes from ...persistence import delete_file @@ -246,7 +251,10 @@ def new_wallet(wallet_type): address_type = request.form["type"] sigs_total = int(request.form.get("sigs_total", 1)) sigs_required = int(request.form.get("sigs_required", 1)) - if alias(wallet_name) in app.specter.wallet_manager.wallets_aliases: + if ( + create_unique_id(wallet_name) + in app.specter.wallet_manager.wallets_aliases + ): err = _("Wallet name already exists. Choose a different name!") if err: devices = [ @@ -383,6 +391,7 @@ def new_wallet(wallet_type): ################## Wallet pages ####################### + ###### Wallet index page ###### @wallets_endpoint.route("/wallet//") @login_required @@ -788,6 +797,13 @@ def addresses(wallet_alias): @login_required def settings(wallet_alias): wallet: Wallet = app.specter.wallet_manager.get_by_alias(wallet_alias) + + # Check whether wallet has at least one device which supports multisig registrations (currently only Jade) + has_device_for_multisig_registration = any( + getattr(device, "supports_multisig_registration", False) + for device in wallet.devices + ) + if request.method == "POST": action = request.form["action"] # Would like to refactor this to another endpoint as well @@ -798,7 +814,10 @@ def settings(wallet_alias): flash(_("Wallet name cannot be empty"), "error") elif wallet_name == wallet.name: pass - elif alias(wallet_name) in app.specter.wallet_manager.wallets_aliases: + elif ( + create_unique_id(wallet_name) + in app.specter.wallet_manager.wallets_aliases + ): flash( _("Wallet name already exists. Choose a different name!"), "error" ) @@ -812,6 +831,7 @@ def settings(wallet_alias): purposes=purposes, wallet_alias=wallet_alias, wallet=wallet, + has_device_for_multisig_registration=has_device_for_multisig_registration, specter=app.specter, rand=rand, scroll_to_rescan_blockchain=request.args.get("rescan_blockchain"), diff --git a/src/cryptoadvance/specter/static/hwi.js b/src/cryptoadvance/specter/static/hwi.js index 709359e3b2..a966e266aa 100644 --- a/src/cryptoadvance/specter/static/hwi.js +++ b/src/cryptoadvance/specter/static/hwi.js @@ -1,3 +1,7 @@ +function generateId() { + return Date.now().toString(36) + Math.random().toString(36).substring(2); +} + class HWIBridge { constructor(url, chain) { this.url = url; @@ -5,7 +9,7 @@ class HWIBridge { this.chain = chain; this.in_progress = false; } - async fetch(command, params={}, timeout=0){ + async requestToHwiBridge(command, params={}, timeout=0){ if(this.in_progress){ throw "HWI is busy processing previous request."; } @@ -17,9 +21,10 @@ class HWIBridge { const timeoutId = setTimeout(() => controller.abort(), timeout); } try{ - if (command != 'detect_device' && command != 'enumerate') { + if (command != 'detect_device') { params.chain = this.chain; } + const requestId = generateId() data = await fetch(this.url, { method: 'POST', headers: { @@ -30,7 +35,7 @@ class HWIBridge { body: JSON.stringify({ 'jsonrpc': '2.0', 'method': command, - 'id': 1, + 'id': requestId, params, forwarded_request: (this.url !== '/hwi/api/'), }) @@ -44,13 +49,13 @@ class HWIBridge { return data.result; } async enumerate(passphrase="", useTimeout){ - return await this.fetch("enumerate", { + return await this.requestToHwiBridge("enumerate", { passphrase }, (useTimeout ? 60000 : 0)); } async detectDevice(type, rescan=true){ // TODO: fingerprint, path, type - return await this.fetch("detect_device", + return await this.requestToHwiBridge("detect_device", { device_type: type, rescan_devices: rescan }); } @@ -59,7 +64,7 @@ class HWIBridge { Tells the server to send the 'togglepassphrase' command to the device. KeepKey and Trezor only. **/ - return await this.fetch('toggle_passphrase', { + return await this.requestToHwiBridge('toggle_passphrase', { device_type: device.type, path: device.path }); @@ -70,7 +75,7 @@ class HWIBridge { Asks the HWI server for a pairing code for BitBox02. Returns {"code": ""} with the code or an empty string if none found. **/ - return await this.fetch('bitbox02_pairing', {}); + return await this.requestToHwiBridge('bitbox02_pairing', {}); } async promptPin(device, passphrase="") { @@ -81,7 +86,7 @@ class HWIBridge { if(!('passphrase' in device)){ device.passphrase = passphrase; } - return await this.fetch('prompt_pin', { + return await this.requestToHwiBridge('prompt_pin', { device_type: device.type, path: device.path, passphrase: device.passphrase, @@ -96,7 +101,7 @@ class HWIBridge { if(!('passphrase' in device)){ device.passphrase = passphrase; } - return await this.fetch('send_pin', { + return await this.requestToHwiBridge('send_pin', { device_type: device.type, path: device.path, passphrase: device.passphrase, @@ -111,7 +116,7 @@ class HWIBridge { if(!('passphrase' in device)){ device.passphrase = passphrase; } - return await this.fetch('sign_tx', { + return await this.requestToHwiBridge('sign_tx', { device_type: device.type, path: device.path, passphrase: device.passphrase, @@ -126,7 +131,7 @@ class HWIBridge { if(!('passphrase' in device)){ device.passphrase = passphrase; } - return await this.fetch('sign_message', { + return await this.requestToHwiBridge('sign_message', { device_type: device.type, path: device.path, passphrase: device.passphrase, @@ -139,7 +144,7 @@ class HWIBridge { if(!('passphrase' in device)){ device.passphrase = passphrase; } - return await this.fetch('extract_xpubs', { + return await this.requestToHwiBridge('extract_xpubs', { device_type: device.type, account: account, path: device.path, @@ -153,7 +158,7 @@ class HWIBridge { if(!('passphrase' in device)){ device.passphrase = passphrase; } - return await this.fetch('extract_xpub', { + return await this.requestToHwiBridge('extract_xpub', { device_type: device.type, derivation: derivation, path: device.path, @@ -166,7 +171,7 @@ class HWIBridge { if(!('passphrase' in device)){ device.passphrase = passphrase; } - return await this.fetch('extract_master_blinding_key', { + return await this.requestToHwiBridge('extract_master_blinding_key', { device_type: device.type, path: device.path, passphrase: device.passphrase, @@ -179,7 +184,7 @@ class HWIBridge { if(!('passphrase' in device)){ device.passphrase = passphrase; } - return await this.fetch('display_address', { + return await this.requestToHwiBridge('display_address', { device_type: device.type, path: device.path, passphrase: device.passphrase, @@ -187,4 +192,14 @@ class HWIBridge { xpubs_descriptor: xpubs_descriptor, }); } + + async registerMultisig(device, descriptor, fingerprint) { + return await this.requestToHwiBridge('register_multisig', { + device_type: device.type, + path: device.path, + passphrase: device.passphrase, + fingerprint: device.fingerprint, + descriptor: descriptor, + }) + } } diff --git a/src/cryptoadvance/specter/templates/includes/hwi/components/wallet.jinja b/src/cryptoadvance/specter/templates/includes/hwi/components/wallet.jinja index 16544176d9..d29a329fdc 100644 --- a/src/cryptoadvance/specter/templates/includes/hwi/components/wallet.jinja +++ b/src/cryptoadvance/specter/templates/includes/hwi/components/wallet.jinja @@ -96,6 +96,36 @@ } } } + + async function registerMultisig(descriptor, fingerprint) { + const devices = await enumerate() + + if (!devices || devices.length === 0) { + return + } + + const device = await selectDevice(devices) + + if (!device) { + return + } + + if (fingerprint && device.fingerprint != fingerprint) { + handleHWIError("Device fingerprints don't match. You have probably selected the wrong device.") + return + } + + showHWIProgress("Registering multisig ...", `Confirm on your ${capitalize(device.type)}`) + + try { + await hwi.registerMultisig(device, descriptor) + } catch (error) { + handleHWIError(error) + return + } + hidePageOverlay() + showNotification("Multisig registered successfully!", 3000); + } {% endif %} diff --git a/src/cryptoadvance/specter/templates/includes/hwi/hwi.jinja b/src/cryptoadvance/specter/templates/includes/hwi/hwi.jinja index 33c7aae2c7..1b3f08c8e9 100644 --- a/src/cryptoadvance/specter/templates/includes/hwi/hwi.jinja +++ b/src/cryptoadvance/specter/templates/includes/hwi/hwi.jinja @@ -93,7 +93,7 @@ let retryCounter = 0; try { try { - result = await hwi.enumerate(passphrase, deviceTypes == 'bitbox02'); + result = await hwi.enumerate(passphrase, true); } catch (e) { if (e.message !== 'Fetch is aborted' && e.message !== 'The user aborted a request.') { throw e; @@ -122,7 +122,7 @@ } console.log("Retrying enumerate..."); try { - result = await hwi.enumerate(passphrase, deviceTypes == 'bitbox02'); + result = await hwi.enumerate(passphrase, true); } catch (e) { if (e.message !== 'Fetch is aborted' && e.message !== 'The user aborted a request.') { throw e; diff --git a/src/cryptoadvance/specter/templates/includes/message-box.html b/src/cryptoadvance/specter/templates/includes/message-box.html index 6f84948a41..ea3d5130d2 100644 --- a/src/cryptoadvance/specter/templates/includes/message-box.html +++ b/src/cryptoadvance/specter/templates/includes/message-box.html @@ -4,10 +4,23 @@ error happened!!! --> @@ -21,8 +34,8 @@ var style = document.getElementById("messagebox").content; var clone = style.cloneNode(true); this.main = clone.querySelector("#main"); - this.text = clone.getElementById('error-text') - this.cancelIcon = clone.getElementById('cancel-icon') + this.text = clone.getElementById("error-text"); + this.cancelIcon = clone.getElementById("cancel-icon"); clone.querySelector("a").addEventListener("click", (e) => { this.close(); }); @@ -46,21 +59,20 @@ }, 200); } setType(type) { - if (type === 'error') { + if (type === "error") { this.main.classList.remove("bg-dark-700"); this.text.classList.remove("text-white"); - this.cancelIcon.classList.remove("text-white") + this.cancelIcon.classList.remove("text-white"); this.main.classList.add("bg-red-100"); this.text.classList.add("text-red-700"); - this.cancelIcon.classList.add("text-red-700") - } - else if (type == 'warning') { + this.cancelIcon.classList.add("text-red-700"); + } else if (type == "warning") { this.main.classList.remove("bg-dark-700"); this.text.classList.remove("text-white"); - this.cancelIcon.classList.remove("text-white") + this.cancelIcon.classList.remove("text-white"); this.main.classList.add("bg-orange-100"); this.text.classList.add("text-orange-700"); - this.cancelIcon.classList.add("text-orange-700") + this.cancelIcon.classList.add("text-orange-700"); } } connectedCallback() { diff --git a/src/cryptoadvance/specter/templates/includes/overlay/overlay.html b/src/cryptoadvance/specter/templates/includes/overlay/overlay.html index 31cc0e953d..ae929d8a9e 100644 --- a/src/cryptoadvance/specter/templates/includes/overlay/overlay.html +++ b/src/cryptoadvance/specter/templates/includes/overlay/overlay.html @@ -1,13 +1,9 @@