beanhub-cli 1.4.2__tar.gz → 1.5.0b2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/PKG-INFO +8 -1
- beanhub_cli-1.5.0b2/beanhub_cli/config.py +49 -0
- beanhub_cli-1.5.0b2/beanhub_cli/connect/cli.py +11 -0
- beanhub_cli-1.5.0b2/beanhub_cli/connect/config.py +30 -0
- beanhub_cli-1.5.0b2/beanhub_cli/connect/encryption.py +25 -0
- beanhub_cli-1.5.0b2/beanhub_cli/connect/main.py +301 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/environment.py +2 -0
- beanhub_cli-1.5.0b2/beanhub_cli/forms/app/__init__.py +0 -0
- beanhub_cli-1.5.0b2/beanhub_cli/login.py +88 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/main.py +2 -0
- beanhub_cli-1.5.0b2/beanhub_cli/utils.py +24 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/pyproject.toml +9 -2
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/LICENSE +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/README.md +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/__init__.py +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/aliase.py +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/cli.py +0 -0
- {beanhub_cli-1.4.2/beanhub_cli/forms → beanhub_cli-1.5.0b2/beanhub_cli/connect}/__init__.py +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/format.py +0 -0
- {beanhub_cli-1.4.2/beanhub_cli/forms/app → beanhub_cli-1.5.0b2/beanhub_cli/forms}/__init__.py +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/constants.py +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/data/sample_forms_doc.yaml +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/deps.py +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/helpers.py +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/main.py +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/routes.py +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/settings.py +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/js/beanhub-web-react-0.3.0.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/js/beanhub-web-react-0.3.0.js.LICENSE.txt +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/js/beanhub-web-react-0.3.0.js.map +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/bootstrap-datepicker/LICENSE +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/bootstrap-datepicker/css/bootstrap-datepicker.css +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/bootstrap-datepicker/css/bootstrap-datepicker.css.map +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/bootstrap-datepicker/css/bootstrap-datepicker.min.css +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/bootstrap-datepicker/js/bootstrap-datepicker.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/bootstrap-datepicker/js/bootstrap-datepicker.min.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/bootstrap-datepicker/locales/bootstrap-datepicker-en-CA.min.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/bootstrap-datepicker/locales/bootstrap-datepicker.ar-DZ.min.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/bootstrap-datepicker/locales/bootstrap-datepicker.ar-tn.min.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/bootstrap-datepicker/locales/bootstrap-datepicker.ar.min.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/bootstrap-datepicker/locales/bootstrap-datepicker.az.min.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/bootstrap-datepicker/locales/bootstrap-datepicker.bg.min.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/bootstrap-datepicker/locales/bootstrap-datepicker.bm.min.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/bootstrap-datepicker/locales/bootstrap-datepicker.bn.min.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/bootstrap-datepicker/locales/bootstrap-datepicker.br.min.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/bootstrap-datepicker/locales/bootstrap-datepicker.bs.min.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/bootstrap-datepicker/locales/bootstrap-datepicker.ca.min.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/bootstrap-datepicker/locales/bootstrap-datepicker.cs.min.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/bootstrap-datepicker/locales/bootstrap-datepicker.cy.min.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/bootstrap-datepicker/locales/bootstrap-datepicker.da.min.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/bootstrap-datepicker/locales/bootstrap-datepicker.de.min.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/bootstrap-datepicker/locales/bootstrap-datepicker.el.min.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/bootstrap-datepicker/locales/bootstrap-datepicker.en-AU.min.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/bootstrap-datepicker/locales/bootstrap-datepicker.en-CA.min.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/bootstrap-datepicker/locales/bootstrap-datepicker.en-GB.min.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/bootstrap-datepicker/locales/bootstrap-datepicker.en-IE.min.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/bootstrap-datepicker/locales/bootstrap-datepicker.en-NZ.min.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/bootstrap-datepicker/locales/bootstrap-datepicker.en-US.min.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/bootstrap-datepicker/locales/bootstrap-datepicker.en-ZA.min.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/bootstrap-datepicker/locales/bootstrap-datepicker.eo.min.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/bootstrap-datepicker/locales/bootstrap-datepicker.es.min.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/bootstrap-datepicker/locales/bootstrap-datepicker.et.min.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/bootstrap-datepicker/locales/bootstrap-datepicker.eu.min.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/bootstrap-datepicker/locales/bootstrap-datepicker.fa.min.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/bootstrap-datepicker/locales/bootstrap-datepicker.fi.min.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/bootstrap-datepicker/locales/bootstrap-datepicker.fo.min.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/bootstrap-datepicker/locales/bootstrap-datepicker.fr-CH.min.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/bootstrap-datepicker/locales/bootstrap-datepicker.fr.min.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/bootstrap-datepicker/locales/bootstrap-datepicker.gl.min.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/bootstrap-datepicker/locales/bootstrap-datepicker.he.min.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/bootstrap-datepicker/locales/bootstrap-datepicker.hi.min.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/bootstrap-datepicker/locales/bootstrap-datepicker.hr.min.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/bootstrap-datepicker/locales/bootstrap-datepicker.hu.min.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/bootstrap-datepicker/locales/bootstrap-datepicker.hy.min.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/bootstrap-datepicker/locales/bootstrap-datepicker.id.min.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/bootstrap-datepicker/locales/bootstrap-datepicker.is.min.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/bootstrap-datepicker/locales/bootstrap-datepicker.it-CH.min.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/bootstrap-datepicker/locales/bootstrap-datepicker.it.min.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/bootstrap-datepicker/locales/bootstrap-datepicker.ja.min.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/bootstrap-datepicker/locales/bootstrap-datepicker.ka.min.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/bootstrap-datepicker/locales/bootstrap-datepicker.kh.min.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/bootstrap-datepicker/locales/bootstrap-datepicker.kk.min.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/bootstrap-datepicker/locales/bootstrap-datepicker.km.min.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/bootstrap-datepicker/locales/bootstrap-datepicker.ko.min.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/bootstrap-datepicker/locales/bootstrap-datepicker.kr.min.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/bootstrap-datepicker/locales/bootstrap-datepicker.lt.min.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/bootstrap-datepicker/locales/bootstrap-datepicker.lv.min.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/bootstrap-datepicker/locales/bootstrap-datepicker.me.min.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/bootstrap-datepicker/locales/bootstrap-datepicker.mk.min.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/bootstrap-datepicker/locales/bootstrap-datepicker.mn.min.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/bootstrap-datepicker/locales/bootstrap-datepicker.mr.min.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/bootstrap-datepicker/locales/bootstrap-datepicker.ms.min.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/bootstrap-datepicker/locales/bootstrap-datepicker.nl-BE.min.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/bootstrap-datepicker/locales/bootstrap-datepicker.nl.min.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/bootstrap-datepicker/locales/bootstrap-datepicker.no.min.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/bootstrap-datepicker/locales/bootstrap-datepicker.oc.min.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/bootstrap-datepicker/locales/bootstrap-datepicker.pl.min.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/bootstrap-datepicker/locales/bootstrap-datepicker.pt-BR.min.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/bootstrap-datepicker/locales/bootstrap-datepicker.pt.min.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/bootstrap-datepicker/locales/bootstrap-datepicker.ro.min.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/bootstrap-datepicker/locales/bootstrap-datepicker.rs-latin.min.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/bootstrap-datepicker/locales/bootstrap-datepicker.rs.min.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/bootstrap-datepicker/locales/bootstrap-datepicker.ru.min.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/bootstrap-datepicker/locales/bootstrap-datepicker.si.min.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/bootstrap-datepicker/locales/bootstrap-datepicker.sk.min.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/bootstrap-datepicker/locales/bootstrap-datepicker.sl.min.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/bootstrap-datepicker/locales/bootstrap-datepicker.sq.min.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/bootstrap-datepicker/locales/bootstrap-datepicker.sr-latin.min.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/bootstrap-datepicker/locales/bootstrap-datepicker.sr.min.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/bootstrap-datepicker/locales/bootstrap-datepicker.sv.min.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/bootstrap-datepicker/locales/bootstrap-datepicker.sw.min.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/bootstrap-datepicker/locales/bootstrap-datepicker.ta.min.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/bootstrap-datepicker/locales/bootstrap-datepicker.tg.min.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/bootstrap-datepicker/locales/bootstrap-datepicker.th.min.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/bootstrap-datepicker/locales/bootstrap-datepicker.tk.min.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/bootstrap-datepicker/locales/bootstrap-datepicker.tr.min.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/bootstrap-datepicker/locales/bootstrap-datepicker.uk.min.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/bootstrap-datepicker/locales/bootstrap-datepicker.uz-cyrl.min.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/bootstrap-datepicker/locales/bootstrap-datepicker.uz-latn.min.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/bootstrap-datepicker/locales/bootstrap-datepicker.vi.min.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/bootstrap-datepicker/locales/bootstrap-datepicker.zh-CN.min.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/bootstrap-datepicker/locales/bootstrap-datepicker.zh-TW.min.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/fontawesome6/LICENSE.txt +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/fontawesome6/css/all.min.css +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/fontawesome6/webfonts/fa-brands-400.ttf +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/fontawesome6/webfonts/fa-brands-400.woff2 +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/fontawesome6/webfonts/fa-regular-400.ttf +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/fontawesome6/webfonts/fa-regular-400.woff2 +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/fontawesome6/webfonts/fa-solid-900.ttf +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/fontawesome6/webfonts/fa-solid-900.woff2 +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/fontawesome6/webfonts/fa-v4compatibility.ttf +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/fontawesome6/webfonts/fa-v4compatibility.woff2 +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/LICENSE.md +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/css/select2.css +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/css/select2.min.css +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/i18n/af.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/i18n/ar.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/i18n/az.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/i18n/bg.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/i18n/bn.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/i18n/bs.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/i18n/ca.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/i18n/cs.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/i18n/da.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/i18n/de.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/i18n/dsb.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/i18n/el.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/i18n/en.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/i18n/eo.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/i18n/es.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/i18n/et.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/i18n/eu.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/i18n/fa.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/i18n/fi.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/i18n/fr.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/i18n/gl.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/i18n/he.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/i18n/hi.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/i18n/hr.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/i18n/hsb.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/i18n/hu.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/i18n/hy.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/i18n/id.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/i18n/is.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/i18n/it.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/i18n/ja.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/i18n/ka.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/i18n/km.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/i18n/ko.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/i18n/lt.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/i18n/lv.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/i18n/mk.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/i18n/ms.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/i18n/nb.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/i18n/ne.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/i18n/nl.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/i18n/pa.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/i18n/pl.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/i18n/ps.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/i18n/pt-BR.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/i18n/pt.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/i18n/ro.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/i18n/ru.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/i18n/sk.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/i18n/sl.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/i18n/sq.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/i18n/sr-Cyrl.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/i18n/sr.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/i18n/sv.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/i18n/te.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/i18n/th.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/i18n/tk.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/i18n/tr.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/i18n/uk.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/i18n/vi.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/i18n/zh-CN.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/i18n/zh-TW.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/select2.full.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/select2.full.min.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/select2.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/select2.min.js +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/templates/errors.html +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/templates/flash_messages.html +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/templates/footer.html +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/templates/form.html +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/templates/home.html +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/templates/layout.html +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/cli.py +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/main.py +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/validator.py +0 -0
- {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/import_cli.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: beanhub-cli
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.5.0b2
|
|
4
4
|
Summary: Command line tools for BeanHub
|
|
5
5
|
License: MIT
|
|
6
6
|
Author: Fang-Pen Lin
|
|
@@ -11,19 +11,26 @@ Classifier: Programming Language :: Python :: 3
|
|
|
11
11
|
Classifier: Programming Language :: Python :: 3.10
|
|
12
12
|
Classifier: Programming Language :: Python :: 3.11
|
|
13
13
|
Classifier: Programming Language :: Python :: 3.12
|
|
14
|
+
Provides-Extra: connect
|
|
15
|
+
Provides-Extra: login
|
|
14
16
|
Requires-Dist: beancount-black (>=1.0.1,<2.0.0)
|
|
15
17
|
Requires-Dist: beancount-parser (>=1.2.3,<1.3.0)
|
|
16
18
|
Requires-Dist: beanhub-extract (>=0.1.0,<1.0.0)
|
|
17
19
|
Requires-Dist: beanhub-forms (>=0.1.3,<0.2.0)
|
|
18
20
|
Requires-Dist: beanhub-import (>=1.0.0,<2.0.0)
|
|
19
21
|
Requires-Dist: click (>=8.1.7,<9.0.0)
|
|
22
|
+
Requires-Dist: cryptography (>=44.0.0,<45.0.0) ; extra == "connect"
|
|
20
23
|
Requires-Dist: fastapi (>=0.98.0,<1.0.0)
|
|
21
24
|
Requires-Dist: jinja2 (>=3.1.2,<4.0.0)
|
|
22
25
|
Requires-Dist: pydantic (>=2.4.0,<3.0.0)
|
|
23
26
|
Requires-Dist: pydantic-settings (>=2.0.3,<3.0.0)
|
|
27
|
+
Requires-Dist: pynacl (>=1.5.0,<2.0.0) ; extra == "connect"
|
|
24
28
|
Requires-Dist: pyyaml (>=6.0.1,<7.0.0)
|
|
29
|
+
Requires-Dist: requests (>=2.32.3,<3.0.0) ; extra == "login" or extra == "connect"
|
|
25
30
|
Requires-Dist: rich (>=13.6.0,<14.0.0)
|
|
26
31
|
Requires-Dist: starlette-wtf (>=0.4.3,<0.5.0)
|
|
32
|
+
Requires-Dist: tomli (>=2.2.1,<3.0.0) ; extra == "login" or extra == "connect"
|
|
33
|
+
Requires-Dist: tomli-w (>=1.1.0,<2.0.0) ; extra == "login" or extra == "connect"
|
|
27
34
|
Requires-Dist: uvicorn (>=0.23.2,<1.0.0)
|
|
28
35
|
Project-URL: Documentation, https://beanhub-cli-docs.beanhub.io
|
|
29
36
|
Description-Content-Type: text/markdown
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import pathlib
|
|
2
|
+
|
|
3
|
+
from pydantic import BaseModel
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class AccessToken(BaseModel):
|
|
7
|
+
token: str
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class Repository(BaseModel):
|
|
11
|
+
default: str | None = None
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class Config(BaseModel):
|
|
15
|
+
access_token: AccessToken | None = None
|
|
16
|
+
repo: Repository | None = None
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def get_config_path() -> pathlib.Path:
|
|
20
|
+
return pathlib.Path.home() / ".beanhub" / "config.toml"
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def load_config(file_path: pathlib.Path | None = None) -> Config | None:
|
|
24
|
+
import tomli
|
|
25
|
+
|
|
26
|
+
if file_path is None:
|
|
27
|
+
file_path = get_config_path()
|
|
28
|
+
if not file_path.exists():
|
|
29
|
+
return
|
|
30
|
+
|
|
31
|
+
with file_path.open("rb") as fo:
|
|
32
|
+
obj = tomli.load(fo)
|
|
33
|
+
return Config.model_validate(obj)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def save_config(config: Config, file_path: pathlib.Path | None = None):
|
|
37
|
+
import tomli_w
|
|
38
|
+
|
|
39
|
+
if file_path is None:
|
|
40
|
+
file_path = get_config_path()
|
|
41
|
+
file_path.parent.mkdir(parents=True, exist_ok=True)
|
|
42
|
+
|
|
43
|
+
file_path.write_text("")
|
|
44
|
+
file_path.chmod(0o600)
|
|
45
|
+
# TODO: for now we save the config as plaintext. We should consider to use OS keychain service in the future
|
|
46
|
+
# for better security
|
|
47
|
+
with file_path.open("wb") as fo:
|
|
48
|
+
obj = config.model_dump(mode="json")
|
|
49
|
+
tomli_w.dump(obj, fo)
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import dataclasses
|
|
2
|
+
import sys
|
|
3
|
+
|
|
4
|
+
from ..config import load_config
|
|
5
|
+
from ..environment import Environment
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@dataclasses.dataclass
|
|
9
|
+
class ConnectConfig:
|
|
10
|
+
token: str
|
|
11
|
+
repo: str
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
# TODO: maybe extract this part to a shared env for connect command?
|
|
15
|
+
def ensure_config(env: Environment, repo: str | None) -> ConnectConfig:
|
|
16
|
+
config = load_config()
|
|
17
|
+
if config is None or config.access_token is None:
|
|
18
|
+
env.logger.error(
|
|
19
|
+
'You need to login into your BeanHub account with "bh login" command first'
|
|
20
|
+
)
|
|
21
|
+
sys.exit(-1)
|
|
22
|
+
if repo is None and (config.repo is None or config.repo.default is None):
|
|
23
|
+
env.logger.error(
|
|
24
|
+
'You need to provide a repo by -r argument, such as "myuser/myrepo" or define a default repo in your config file'
|
|
25
|
+
)
|
|
26
|
+
sys.exit(-1)
|
|
27
|
+
return ConnectConfig(
|
|
28
|
+
token=config.access_token.token,
|
|
29
|
+
repo=repo if repo is not None else config.repo.default,
|
|
30
|
+
)
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import io
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def decrypt_file(
|
|
5
|
+
input_file: io.BytesIO, output_file: io.BytesIO, iv: bytes, key: bytes
|
|
6
|
+
):
|
|
7
|
+
from cryptography.hazmat.primitives import padding
|
|
8
|
+
from cryptography.hazmat.primitives.ciphers import algorithms
|
|
9
|
+
from cryptography.hazmat.primitives.ciphers import Cipher
|
|
10
|
+
from cryptography.hazmat.primitives.ciphers import modes
|
|
11
|
+
|
|
12
|
+
cipher = Cipher(algorithms.AES256(key), modes.CBC(iv))
|
|
13
|
+
decryptor = cipher.decryptor()
|
|
14
|
+
padder = padding.PKCS7(128).unpadder()
|
|
15
|
+
while True:
|
|
16
|
+
chunk = input_file.read(4096)
|
|
17
|
+
if not chunk:
|
|
18
|
+
break
|
|
19
|
+
decrypted = decryptor.update(chunk)
|
|
20
|
+
unpadded_chunk = padder.update(decrypted)
|
|
21
|
+
output_file.write(unpadded_chunk)
|
|
22
|
+
output_file.write(padder.update(decryptor.finalize()))
|
|
23
|
+
output_file.write(padder.finalize())
|
|
24
|
+
output_file.flush()
|
|
25
|
+
output_file.seek(0)
|
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
import enum
|
|
2
|
+
import json
|
|
3
|
+
import sys
|
|
4
|
+
import tarfile
|
|
5
|
+
import tempfile
|
|
6
|
+
import time
|
|
7
|
+
import urllib.parse
|
|
8
|
+
|
|
9
|
+
import click
|
|
10
|
+
import rich
|
|
11
|
+
from rich import box
|
|
12
|
+
from rich.markup import escape
|
|
13
|
+
from rich.padding import Padding
|
|
14
|
+
from rich.table import Table
|
|
15
|
+
|
|
16
|
+
from ..environment import Environment
|
|
17
|
+
from ..environment import pass_env
|
|
18
|
+
from ..utils import check_imports
|
|
19
|
+
from .cli import cli
|
|
20
|
+
from .config import ConnectConfig
|
|
21
|
+
from .config import ensure_config
|
|
22
|
+
from .encryption import decrypt_file
|
|
23
|
+
|
|
24
|
+
TABLE_HEADER_STYLE = "yellow"
|
|
25
|
+
TABLE_COLUMN_STYLE = "cyan"
|
|
26
|
+
SPOOLED_FILE_MAX_SIZE = 1024 * 1024 * 5
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@enum.unique
|
|
30
|
+
class PlaidItemSyncState(enum.Enum):
|
|
31
|
+
PENDING = "PENDING"
|
|
32
|
+
PROCESSING = "PROCESSING"
|
|
33
|
+
SYNC_COMPLETE = "SYNC_COMPLETE"
|
|
34
|
+
SYNC_FAILED = "SYNC_FAILED"
|
|
35
|
+
IMPORT_COMPLETE = "IMPORT_COMPLETE"
|
|
36
|
+
IMPORT_COMPLETE_NO_CHANGES = "IMPORT_COMPLETE_NO_CHANGES"
|
|
37
|
+
IMPORT_FAILED = "IMPORT_FAILED"
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
@enum.unique
|
|
41
|
+
class DumpRequestState(enum.Enum):
|
|
42
|
+
PENDING = "PENDING"
|
|
43
|
+
PROCESSING = "PROCESSING"
|
|
44
|
+
COMPLETE = "COMPLETE"
|
|
45
|
+
FAILED = "FAILED"
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
GOOD_TERMINAL_SYNC_STATES = frozenset(
|
|
49
|
+
[
|
|
50
|
+
PlaidItemSyncState.IMPORT_COMPLETE,
|
|
51
|
+
PlaidItemSyncState.IMPORT_COMPLETE_NO_CHANGES,
|
|
52
|
+
]
|
|
53
|
+
)
|
|
54
|
+
BAD_TERMINAL_SYNC_STATES = frozenset(
|
|
55
|
+
[
|
|
56
|
+
PlaidItemSyncState.IMPORT_FAILED,
|
|
57
|
+
PlaidItemSyncState.SYNC_FAILED,
|
|
58
|
+
]
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def run_sync(env: Environment, config: ConnectConfig):
|
|
63
|
+
import requests
|
|
64
|
+
|
|
65
|
+
env.logger.info(
|
|
66
|
+
"Running sync batch for repo [green]%s[/]",
|
|
67
|
+
config.repo,
|
|
68
|
+
extra={"markup": True, "highlighter": None},
|
|
69
|
+
)
|
|
70
|
+
# TODO: generate API client from OpenAPI spec instead
|
|
71
|
+
url = urllib.parse.urljoin(
|
|
72
|
+
env.api_base_url, f"v1/repos/{config.repo}/connect/sync_batches"
|
|
73
|
+
)
|
|
74
|
+
resp = requests.post(url, headers={"access-token": config.token})
|
|
75
|
+
if resp.status_code == 422:
|
|
76
|
+
env.logger.error("Failed to sync with error: %s", resp.json())
|
|
77
|
+
sys.exit(-1)
|
|
78
|
+
elif resp.status_code == 401:
|
|
79
|
+
env.logger.error(
|
|
80
|
+
"Failed to sync permission error: %s, please ensure your Access Token has API_CONNECT_SYNC permission",
|
|
81
|
+
resp.json(),
|
|
82
|
+
)
|
|
83
|
+
sys.exit(-1)
|
|
84
|
+
resp.raise_for_status()
|
|
85
|
+
|
|
86
|
+
batch_id = resp.json()["id"]
|
|
87
|
+
env.logger.info(
|
|
88
|
+
"Created sync batch [green]%s[/], waiting for updates ...",
|
|
89
|
+
batch_id,
|
|
90
|
+
extra={"markup": True, "highlighter": None},
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
url = urllib.parse.urljoin(
|
|
94
|
+
env.api_base_url, f"v1/repos/{config.repo}/connect/sync_batches/{batch_id}"
|
|
95
|
+
)
|
|
96
|
+
while True:
|
|
97
|
+
time.sleep(5)
|
|
98
|
+
resp = requests.get(url, headers={"access-token": config.token})
|
|
99
|
+
# TODO: provide friendly error messages here
|
|
100
|
+
resp.raise_for_status()
|
|
101
|
+
payload = resp.json()
|
|
102
|
+
total = len(payload["syncs"])
|
|
103
|
+
good_terms = list(
|
|
104
|
+
sync
|
|
105
|
+
for sync in payload["syncs"]
|
|
106
|
+
if PlaidItemSyncState[sync["state"]] in GOOD_TERMINAL_SYNC_STATES
|
|
107
|
+
)
|
|
108
|
+
bad_terms = list(
|
|
109
|
+
sync
|
|
110
|
+
for sync in payload["syncs"]
|
|
111
|
+
if PlaidItemSyncState[sync["state"]] in BAD_TERMINAL_SYNC_STATES
|
|
112
|
+
)
|
|
113
|
+
progress = len(good_terms) + len(bad_terms)
|
|
114
|
+
if progress >= total:
|
|
115
|
+
break
|
|
116
|
+
else:
|
|
117
|
+
env.logger.info(
|
|
118
|
+
"Still processing, [green]%s[/] out of [green]%s[/]",
|
|
119
|
+
progress,
|
|
120
|
+
total,
|
|
121
|
+
extra={"markup": True, "highlighter": None},
|
|
122
|
+
)
|
|
123
|
+
table = Table(
|
|
124
|
+
title="Sync finished successfully",
|
|
125
|
+
box=box.SIMPLE,
|
|
126
|
+
header_style=TABLE_HEADER_STYLE,
|
|
127
|
+
expand=True,
|
|
128
|
+
)
|
|
129
|
+
table.add_column("Id", style=TABLE_COLUMN_STYLE)
|
|
130
|
+
table.add_column("Institution", style=TABLE_COLUMN_STYLE)
|
|
131
|
+
table.add_column("State", style=TABLE_COLUMN_STYLE)
|
|
132
|
+
for sync in good_terms:
|
|
133
|
+
table.add_row(
|
|
134
|
+
escape(sync["id"]),
|
|
135
|
+
escape(sync["item"]["institution_name"]),
|
|
136
|
+
escape(sync["state"]),
|
|
137
|
+
)
|
|
138
|
+
rich.print(Padding(table, (1, 0, 0, 4)))
|
|
139
|
+
|
|
140
|
+
table = Table(
|
|
141
|
+
title="Sync finished with error",
|
|
142
|
+
box=box.SIMPLE,
|
|
143
|
+
header_style=TABLE_HEADER_STYLE,
|
|
144
|
+
expand=True,
|
|
145
|
+
)
|
|
146
|
+
table.add_column("Id", style=TABLE_COLUMN_STYLE)
|
|
147
|
+
table.add_column("Institution", style=TABLE_COLUMN_STYLE)
|
|
148
|
+
table.add_column("State", style=TABLE_COLUMN_STYLE)
|
|
149
|
+
table.add_column("Error", style=TABLE_COLUMN_STYLE)
|
|
150
|
+
for sync in bad_terms:
|
|
151
|
+
table.add_row(
|
|
152
|
+
escape(sync["id"]),
|
|
153
|
+
escape(sync["item"]["institution_name"]),
|
|
154
|
+
escape(sync["state"]),
|
|
155
|
+
escape(sync["error_message"]),
|
|
156
|
+
)
|
|
157
|
+
rich.print(Padding(table, (1, 0, 0, 4)))
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
@cli.command(help="Sync transactions for all BeanHub Connect banks")
|
|
161
|
+
@click.option(
|
|
162
|
+
"-r",
|
|
163
|
+
"--repo",
|
|
164
|
+
type=str,
|
|
165
|
+
help='Which repository to run sync on, in "<username>/<repo_name>" format',
|
|
166
|
+
)
|
|
167
|
+
@pass_env
|
|
168
|
+
def sync(env: Environment, repo: str | None):
|
|
169
|
+
check_imports(
|
|
170
|
+
logger=env.logger,
|
|
171
|
+
module_names=["requests", "tomli", "tomli_w"],
|
|
172
|
+
required_extras=["login"],
|
|
173
|
+
)
|
|
174
|
+
config = ensure_config(env, repo=repo)
|
|
175
|
+
run_sync(env, config)
|
|
176
|
+
env.logger.info("done")
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
@cli.command(
|
|
180
|
+
help="Dump transaction CSV files from BeanHub Connect to your local environment"
|
|
181
|
+
)
|
|
182
|
+
@click.option(
|
|
183
|
+
"-r",
|
|
184
|
+
"--repo",
|
|
185
|
+
type=str,
|
|
186
|
+
help='Which repository to run sync on, in "<username>/<repo_name>" format',
|
|
187
|
+
)
|
|
188
|
+
@click.option(
|
|
189
|
+
"-s",
|
|
190
|
+
"--sync",
|
|
191
|
+
type=bool,
|
|
192
|
+
is_flag=True,
|
|
193
|
+
help="Run sync first before running dump",
|
|
194
|
+
)
|
|
195
|
+
@click.option(
|
|
196
|
+
"--unsafe-tar-extract",
|
|
197
|
+
type=bool,
|
|
198
|
+
is_flag=True,
|
|
199
|
+
help="Allow unsafe tar extraction, mostly for Python < 3.11",
|
|
200
|
+
)
|
|
201
|
+
@pass_env
|
|
202
|
+
def dump(env: Environment, repo: str | None, sync: bool, unsafe_tar_extract: bool):
|
|
203
|
+
check_imports(
|
|
204
|
+
logger=env.logger,
|
|
205
|
+
module_names=["requests", "tomli", "tomli_w", "nacl", "cryptography"],
|
|
206
|
+
required_extras=["connect"],
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
import requests
|
|
210
|
+
from nacl.encoding import URLSafeBase64Encoder
|
|
211
|
+
from nacl.public import PrivateKey
|
|
212
|
+
from nacl.public import SealedBox
|
|
213
|
+
|
|
214
|
+
if not hasattr(tarfile, "data_filter") and not unsafe_tar_extract:
|
|
215
|
+
env.logger.error(
|
|
216
|
+
"You need to use Python >= 3.11 in order to safely unpack the downloaded tar file, or you need to pass "
|
|
217
|
+
"in --unsafe-tar-extract argument to allow unsafe tar file extracting"
|
|
218
|
+
)
|
|
219
|
+
sys.exit(-1)
|
|
220
|
+
config = ensure_config(env, repo=repo)
|
|
221
|
+
if sync:
|
|
222
|
+
run_sync(env, config)
|
|
223
|
+
|
|
224
|
+
private_key = PrivateKey.generate()
|
|
225
|
+
public_key = private_key.public_key.encode(URLSafeBase64Encoder).decode("ascii")
|
|
226
|
+
|
|
227
|
+
# TODO: generate API client from OpenAPI spec instead
|
|
228
|
+
url = urllib.parse.urljoin(
|
|
229
|
+
env.api_base_url, f"v1/repos/{config.repo}/connect/dumps"
|
|
230
|
+
)
|
|
231
|
+
resp = requests.post(
|
|
232
|
+
url,
|
|
233
|
+
json=dict(public_key=public_key),
|
|
234
|
+
headers={"access-token": config.token},
|
|
235
|
+
)
|
|
236
|
+
if resp.status_code == 422:
|
|
237
|
+
env.logger.error("Failed to dump with error: %s", resp.json())
|
|
238
|
+
sys.exit(-1)
|
|
239
|
+
elif resp.status_code == 401:
|
|
240
|
+
env.logger.error(
|
|
241
|
+
"Failed to dump permission error: %s, please ensure your Access Token has API_CONNECT_DUMP permission",
|
|
242
|
+
resp.json(),
|
|
243
|
+
)
|
|
244
|
+
sys.exit(-1)
|
|
245
|
+
resp.raise_for_status()
|
|
246
|
+
|
|
247
|
+
dump_id = resp.json()["id"]
|
|
248
|
+
env.logger.info(
|
|
249
|
+
"Created dump [green]%s[/] with public_key [green]%s[/], waiting for updates ...",
|
|
250
|
+
dump_id,
|
|
251
|
+
public_key,
|
|
252
|
+
extra={"markup": True, "highlighter": None},
|
|
253
|
+
)
|
|
254
|
+
|
|
255
|
+
url = urllib.parse.urljoin(
|
|
256
|
+
env.api_base_url, f"v1/repos/{config.repo}/connect/dumps/{dump_id}"
|
|
257
|
+
)
|
|
258
|
+
while True:
|
|
259
|
+
time.sleep(5)
|
|
260
|
+
resp = requests.get(url, headers={"access-token": config.token})
|
|
261
|
+
# TODO: provide friendly error messages here
|
|
262
|
+
resp.raise_for_status()
|
|
263
|
+
payload = resp.json()
|
|
264
|
+
state = DumpRequestState[payload["state"]]
|
|
265
|
+
if state == DumpRequestState.FAILED:
|
|
266
|
+
env.logger.error("Failed to dump with error: %s", payload["error_message"])
|
|
267
|
+
sys.exit(-1)
|
|
268
|
+
elif state == DumpRequestState.COMPLETE:
|
|
269
|
+
break
|
|
270
|
+
else:
|
|
271
|
+
env.logger.debug("State is %s, keep polling...", state)
|
|
272
|
+
|
|
273
|
+
download_url = payload["download_url"]
|
|
274
|
+
sealed_box = SealedBox(private_key)
|
|
275
|
+
encryption_key = json.loads(
|
|
276
|
+
sealed_box.decrypt(URLSafeBase64Encoder.decode(payload["encryption_key"]))
|
|
277
|
+
)
|
|
278
|
+
key = URLSafeBase64Encoder.decode(encryption_key["key"])
|
|
279
|
+
iv = URLSafeBase64Encoder.decode(encryption_key["iv"])
|
|
280
|
+
|
|
281
|
+
with (
|
|
282
|
+
tempfile.SpooledTemporaryFile(SPOOLED_FILE_MAX_SIZE) as encrypted_file,
|
|
283
|
+
tempfile.SpooledTemporaryFile(SPOOLED_FILE_MAX_SIZE) as decrypted_file,
|
|
284
|
+
):
|
|
285
|
+
with (requests.get(download_url, stream=True) as req,):
|
|
286
|
+
for chunk in req.iter_content(4096):
|
|
287
|
+
encrypted_file.write(chunk)
|
|
288
|
+
encrypted_file.flush()
|
|
289
|
+
encrypted_file.seek(0)
|
|
290
|
+
env.logger.info("Decrypting downloaded file ...")
|
|
291
|
+
decrypt_file(
|
|
292
|
+
input_file=encrypted_file, output_file=decrypted_file, key=key, iv=iv
|
|
293
|
+
)
|
|
294
|
+
with tarfile.open(fileobj=decrypted_file, mode="r:gz") as tar_file:
|
|
295
|
+
if hasattr(tarfile, "data_filter"):
|
|
296
|
+
tar_file.extractall(filter="data")
|
|
297
|
+
else:
|
|
298
|
+
env.logger.warning("Performing unsafe tar file extracting")
|
|
299
|
+
tar_file.extractall()
|
|
300
|
+
|
|
301
|
+
env.logger.info("done")
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import dataclasses
|
|
2
2
|
import enum
|
|
3
3
|
import logging
|
|
4
|
+
import os
|
|
4
5
|
|
|
5
6
|
import click
|
|
6
7
|
from beancount_black.formatter import VERBOSE_LOG_LEVEL
|
|
@@ -30,6 +31,7 @@ LOG_LEVEL_MAP = {
|
|
|
30
31
|
class Environment:
|
|
31
32
|
log_level: LogLevel = LogLevel.INFO
|
|
32
33
|
logger: logging.Logger = logging.getLogger("beanhub_cli")
|
|
34
|
+
api_base_url: str = os.getenv("BEANHUB_API_URL", "https://api.beanhub.io")
|
|
33
35
|
|
|
34
36
|
|
|
35
37
|
pass_env = click.make_pass_decorator(Environment, ensure=True)
|
|
File without changes
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import platform
|
|
2
|
+
import time
|
|
3
|
+
import urllib.parse
|
|
4
|
+
import webbrowser
|
|
5
|
+
|
|
6
|
+
from .cli import cli
|
|
7
|
+
from .config import AccessToken
|
|
8
|
+
from .config import Config
|
|
9
|
+
from .config import get_config_path
|
|
10
|
+
from .config import load_config
|
|
11
|
+
from .config import Repository
|
|
12
|
+
from .config import save_config
|
|
13
|
+
from .environment import Environment
|
|
14
|
+
from .environment import pass_env
|
|
15
|
+
from .utils import check_imports
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@cli.command(name="login", help="Login your BeanHub account")
|
|
19
|
+
@pass_env
|
|
20
|
+
def main(env: Environment):
|
|
21
|
+
check_imports(
|
|
22
|
+
logger=env.logger,
|
|
23
|
+
module_names=["requests", "tomli", "tomli_w"],
|
|
24
|
+
required_extras=["login"],
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
import requests
|
|
28
|
+
|
|
29
|
+
config_path = get_config_path()
|
|
30
|
+
config = load_config()
|
|
31
|
+
if config is not None and config.access_token is not None:
|
|
32
|
+
# TODO: ask the user if they want to log out the original session first
|
|
33
|
+
env.logger.error(
|
|
34
|
+
"Already logged in, if you want to login again, please delete the config file at %s first",
|
|
35
|
+
config_path,
|
|
36
|
+
)
|
|
37
|
+
return
|
|
38
|
+
|
|
39
|
+
env.logger.info("Creating auth session ...")
|
|
40
|
+
url = urllib.parse.urljoin(env.api_base_url, "v1/auth/sessions")
|
|
41
|
+
resp = requests.post(url, json=dict(hostname=platform.node()))
|
|
42
|
+
# TODO: check status and provide more user friendly error message?
|
|
43
|
+
resp.raise_for_status()
|
|
44
|
+
|
|
45
|
+
data = resp.json()
|
|
46
|
+
session_id = data["id"]
|
|
47
|
+
code = data["code"]
|
|
48
|
+
poll_url = data["poll_url"]
|
|
49
|
+
auth_url = data["auth_url"]
|
|
50
|
+
|
|
51
|
+
env.logger.info(
|
|
52
|
+
"Auth Code: %s",
|
|
53
|
+
code,
|
|
54
|
+
)
|
|
55
|
+
if not webbrowser.open(data["auth_url"], new=2):
|
|
56
|
+
env.logger.info(
|
|
57
|
+
"Cannot open auth url, please open it manually in your browser: %s",
|
|
58
|
+
auth_url,
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
env.logger.info(
|
|
62
|
+
"Waiting granting access for current auth session: %s ...", session_id
|
|
63
|
+
)
|
|
64
|
+
while True:
|
|
65
|
+
time.sleep(5)
|
|
66
|
+
resp = requests.get(poll_url)
|
|
67
|
+
if resp.status_code == 200:
|
|
68
|
+
payload = resp.json()
|
|
69
|
+
# TODO: ideally this should be a different API call, make it simple for now
|
|
70
|
+
repositories = payload["repositories"]
|
|
71
|
+
save_config(
|
|
72
|
+
Config(
|
|
73
|
+
access_token=AccessToken(token=payload["token"]),
|
|
74
|
+
repo=Repository(default=repositories[0]) if repositories else None,
|
|
75
|
+
)
|
|
76
|
+
)
|
|
77
|
+
env.logger.info("Session access granted, saved config to %s", config_path)
|
|
78
|
+
break
|
|
79
|
+
elif resp.status_code == 202:
|
|
80
|
+
env.logger.debug("Session access not granted yet, try again later")
|
|
81
|
+
else:
|
|
82
|
+
env.logger.error(
|
|
83
|
+
"Failed to fetch token, encountered unexpected status code %s",
|
|
84
|
+
resp.status_code,
|
|
85
|
+
)
|
|
86
|
+
return
|
|
87
|
+
|
|
88
|
+
env.logger.info("done")
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import sys
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def check_imports(
|
|
6
|
+
logger: logging.Logger, module_names: list[str], required_extras: list[str]
|
|
7
|
+
):
|
|
8
|
+
missing_modules = []
|
|
9
|
+
for name in module_names:
|
|
10
|
+
try:
|
|
11
|
+
__import__(name)
|
|
12
|
+
except ImportError:
|
|
13
|
+
missing_modules.append(name)
|
|
14
|
+
if not missing_modules:
|
|
15
|
+
return
|
|
16
|
+
extras = ",".join(required_extras)
|
|
17
|
+
logger.error(
|
|
18
|
+
"Cannot import module %s, please ensure that you install beanhub-cli with optional deps [%s]. "
|
|
19
|
+
'Like `pip install "beanhub-cli[%s]"`',
|
|
20
|
+
missing_modules,
|
|
21
|
+
extras,
|
|
22
|
+
extras,
|
|
23
|
+
)
|
|
24
|
+
sys.exit(-1)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "beanhub-cli"
|
|
3
|
-
version = "1.
|
|
3
|
+
version = "1.5.0b2"
|
|
4
4
|
description = "Command line tools for BeanHub"
|
|
5
5
|
authors = ["Fang-Pen Lin <fangpen@launchplatform.com>"]
|
|
6
6
|
license = "MIT"
|
|
@@ -26,8 +26,15 @@ rich = "^13.6.0"
|
|
|
26
26
|
beanhub-import = ">= 1.0.0, <2.0.0"
|
|
27
27
|
beanhub-extract = ">= 0.1.0, <1.0.0"
|
|
28
28
|
beancount-parser = ">= 1.2.3, <1.3.0"
|
|
29
|
+
requests = { version ="^2.32.3", optional = true }
|
|
30
|
+
tomli = { version = "^2.2.1", optional = true }
|
|
31
|
+
tomli-w = { version = "^1.1.0", optional = true }
|
|
32
|
+
pynacl = { version = "^1.5.0", optional = true }
|
|
33
|
+
cryptography = { version = "^44.0.0", optional = true}
|
|
29
34
|
|
|
30
|
-
[tool.poetry.
|
|
35
|
+
[tool.poetry.extras]
|
|
36
|
+
login = ["requests", "tomli", "tomli-w"]
|
|
37
|
+
connect = ["requests", "tomli", "tomli-w", "pynacl", "cryptography"]
|
|
31
38
|
|
|
32
39
|
[tool.poetry.group.dev.dependencies]
|
|
33
40
|
pytest = "^7.4.1"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{beanhub_cli-1.4.2/beanhub_cli/forms/app → beanhub_cli-1.5.0b2/beanhub_cli/forms}/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/js/beanhub-web-react-0.3.0.js
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|