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.
Files changed (211) hide show
  1. {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/PKG-INFO +8 -1
  2. beanhub_cli-1.5.0b2/beanhub_cli/config.py +49 -0
  3. beanhub_cli-1.5.0b2/beanhub_cli/connect/cli.py +11 -0
  4. beanhub_cli-1.5.0b2/beanhub_cli/connect/config.py +30 -0
  5. beanhub_cli-1.5.0b2/beanhub_cli/connect/encryption.py +25 -0
  6. beanhub_cli-1.5.0b2/beanhub_cli/connect/main.py +301 -0
  7. {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/environment.py +2 -0
  8. beanhub_cli-1.5.0b2/beanhub_cli/forms/app/__init__.py +0 -0
  9. beanhub_cli-1.5.0b2/beanhub_cli/login.py +88 -0
  10. {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/main.py +2 -0
  11. beanhub_cli-1.5.0b2/beanhub_cli/utils.py +24 -0
  12. {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/pyproject.toml +9 -2
  13. {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/LICENSE +0 -0
  14. {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/README.md +0 -0
  15. {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/__init__.py +0 -0
  16. {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/aliase.py +0 -0
  17. {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/cli.py +0 -0
  18. {beanhub_cli-1.4.2/beanhub_cli/forms → beanhub_cli-1.5.0b2/beanhub_cli/connect}/__init__.py +0 -0
  19. {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/format.py +0 -0
  20. {beanhub_cli-1.4.2/beanhub_cli/forms/app → beanhub_cli-1.5.0b2/beanhub_cli/forms}/__init__.py +0 -0
  21. {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/constants.py +0 -0
  22. {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/data/sample_forms_doc.yaml +0 -0
  23. {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/deps.py +0 -0
  24. {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/helpers.py +0 -0
  25. {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/main.py +0 -0
  26. {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/routes.py +0 -0
  27. {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/settings.py +0 -0
  28. {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
  29. {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
  30. {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
  31. {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/bootstrap-datepicker/LICENSE +0 -0
  32. {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/bootstrap-datepicker/css/bootstrap-datepicker.css +0 -0
  33. {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
  34. {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
  35. {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/bootstrap-datepicker/js/bootstrap-datepicker.js +0 -0
  36. {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
  37. {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
  38. {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
  39. {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
  40. {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
  41. {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
  42. {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
  43. {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
  44. {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
  45. {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
  46. {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
  47. {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
  48. {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
  49. {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
  50. {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
  51. {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
  52. {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
  53. {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
  54. {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
  55. {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
  56. {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
  57. {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
  58. {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
  59. {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
  60. {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
  61. {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
  62. {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
  63. {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
  64. {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
  65. {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
  66. {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
  67. {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
  68. {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
  69. {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
  70. {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
  71. {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
  72. {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
  73. {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
  74. {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
  75. {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
  76. {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
  77. {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
  78. {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
  79. {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
  80. {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
  81. {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
  82. {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
  83. {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
  84. {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
  85. {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
  86. {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
  87. {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
  88. {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
  89. {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
  90. {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
  91. {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
  92. {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
  93. {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
  94. {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
  95. {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
  96. {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
  97. {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
  98. {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
  99. {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
  100. {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
  101. {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
  102. {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
  103. {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
  104. {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
  105. {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
  106. {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
  107. {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
  108. {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
  109. {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
  110. {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
  111. {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
  112. {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
  113. {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
  114. {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
  115. {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
  116. {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
  117. {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
  118. {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
  119. {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
  120. {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
  121. {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
  122. {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
  123. {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/fontawesome6/LICENSE.txt +0 -0
  124. {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/fontawesome6/css/all.min.css +0 -0
  125. {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/fontawesome6/webfonts/fa-brands-400.ttf +0 -0
  126. {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/fontawesome6/webfonts/fa-brands-400.woff2 +0 -0
  127. {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/fontawesome6/webfonts/fa-regular-400.ttf +0 -0
  128. {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/fontawesome6/webfonts/fa-regular-400.woff2 +0 -0
  129. {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/fontawesome6/webfonts/fa-solid-900.ttf +0 -0
  130. {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/fontawesome6/webfonts/fa-solid-900.woff2 +0 -0
  131. {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/fontawesome6/webfonts/fa-v4compatibility.ttf +0 -0
  132. {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/fontawesome6/webfonts/fa-v4compatibility.woff2 +0 -0
  133. {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/LICENSE.md +0 -0
  134. {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/css/select2.css +0 -0
  135. {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/css/select2.min.css +0 -0
  136. {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/i18n/af.js +0 -0
  137. {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/i18n/ar.js +0 -0
  138. {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/i18n/az.js +0 -0
  139. {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/i18n/bg.js +0 -0
  140. {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/i18n/bn.js +0 -0
  141. {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/i18n/bs.js +0 -0
  142. {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/i18n/ca.js +0 -0
  143. {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/i18n/cs.js +0 -0
  144. {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/i18n/da.js +0 -0
  145. {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/i18n/de.js +0 -0
  146. {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/i18n/dsb.js +0 -0
  147. {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/i18n/el.js +0 -0
  148. {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/i18n/en.js +0 -0
  149. {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/i18n/eo.js +0 -0
  150. {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/i18n/es.js +0 -0
  151. {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/i18n/et.js +0 -0
  152. {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/i18n/eu.js +0 -0
  153. {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/i18n/fa.js +0 -0
  154. {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/i18n/fi.js +0 -0
  155. {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/i18n/fr.js +0 -0
  156. {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/i18n/gl.js +0 -0
  157. {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/i18n/he.js +0 -0
  158. {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/i18n/hi.js +0 -0
  159. {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/i18n/hr.js +0 -0
  160. {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/i18n/hsb.js +0 -0
  161. {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/i18n/hu.js +0 -0
  162. {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/i18n/hy.js +0 -0
  163. {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/i18n/id.js +0 -0
  164. {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/i18n/is.js +0 -0
  165. {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/i18n/it.js +0 -0
  166. {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/i18n/ja.js +0 -0
  167. {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/i18n/ka.js +0 -0
  168. {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/i18n/km.js +0 -0
  169. {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/i18n/ko.js +0 -0
  170. {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/i18n/lt.js +0 -0
  171. {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/i18n/lv.js +0 -0
  172. {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/i18n/mk.js +0 -0
  173. {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/i18n/ms.js +0 -0
  174. {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/i18n/nb.js +0 -0
  175. {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/i18n/ne.js +0 -0
  176. {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/i18n/nl.js +0 -0
  177. {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/i18n/pa.js +0 -0
  178. {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/i18n/pl.js +0 -0
  179. {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/i18n/ps.js +0 -0
  180. {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/i18n/pt-BR.js +0 -0
  181. {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/i18n/pt.js +0 -0
  182. {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/i18n/ro.js +0 -0
  183. {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/i18n/ru.js +0 -0
  184. {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/i18n/sk.js +0 -0
  185. {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/i18n/sl.js +0 -0
  186. {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/i18n/sq.js +0 -0
  187. {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/i18n/sr-Cyrl.js +0 -0
  188. {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/i18n/sr.js +0 -0
  189. {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/i18n/sv.js +0 -0
  190. {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/i18n/te.js +0 -0
  191. {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/i18n/th.js +0 -0
  192. {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/i18n/tk.js +0 -0
  193. {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/i18n/tr.js +0 -0
  194. {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/i18n/uk.js +0 -0
  195. {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/i18n/vi.js +0 -0
  196. {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/i18n/zh-CN.js +0 -0
  197. {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/i18n/zh-TW.js +0 -0
  198. {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/select2.full.js +0 -0
  199. {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/select2.full.min.js +0 -0
  200. {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/select2.js +0 -0
  201. {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/static/vendor/select2/js/select2.min.js +0 -0
  202. {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/templates/errors.html +0 -0
  203. {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/templates/flash_messages.html +0 -0
  204. {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/templates/footer.html +0 -0
  205. {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/templates/form.html +0 -0
  206. {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/templates/home.html +0 -0
  207. {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/app/templates/layout.html +0 -0
  208. {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/cli.py +0 -0
  209. {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/main.py +0 -0
  210. {beanhub_cli-1.4.2 → beanhub_cli-1.5.0b2}/beanhub_cli/forms/validator.py +0 -0
  211. {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.4.2
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,11 @@
1
+ from ..aliase import AliasedGroup
2
+ from ..cli import cli as root_cli
3
+
4
+
5
+ @root_cli.group(
6
+ name="connect",
7
+ help="BeanHub Connect features, such as sync or dump (login required).",
8
+ cls=AliasedGroup,
9
+ )
10
+ def cli():
11
+ pass
@@ -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")
@@ -1,7 +1,9 @@
1
1
  from . import format # noqa
2
2
  from .cli import cli
3
+ from .connect import main as connect # noqa
3
4
  from .forms import main as forms # noqa
4
5
  from .import_cli import main as import_cli # noqa
6
+ from .login import main as login # noqa
5
7
 
6
8
  __ALL__ = [cli]
7
9
 
@@ -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.4.2"
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.dev-dependencies]
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