bt-cli 0.4.50__tar.gz → 0.4.52__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 (228) hide show
  1. {bt_cli-0.4.50 → bt_cli-0.4.52}/CLAUDE.md +1 -1
  2. {bt_cli-0.4.50 → bt_cli-0.4.52}/PKG-INFO +1 -1
  3. {bt_cli-0.4.50 → bt_cli-0.4.52}/pyproject.toml +1 -1
  4. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/__init__.py +1 -1
  5. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/core/errors.py +13 -0
  6. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/core/prompts.py +16 -2
  7. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/pws/client/passwordsafe.py +29 -0
  8. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/pws/commands/accounts.py +166 -1
  9. {bt_cli-0.4.50 → bt_cli-0.4.52}/.claude/skills/bt/SKILL.md +0 -0
  10. {bt_cli-0.4.50 → bt_cli-0.4.52}/.claude/skills/entitle/SKILL.md +0 -0
  11. {bt_cli-0.4.50 → bt_cli-0.4.52}/.claude/skills/epml/SKILL.md +0 -0
  12. {bt_cli-0.4.50 → bt_cli-0.4.52}/.claude/skills/epmw/SKILL.md +0 -0
  13. {bt_cli-0.4.50 → bt_cli-0.4.52}/.claude/skills/pra/SKILL.md +0 -0
  14. {bt_cli-0.4.50 → bt_cli-0.4.52}/.claude/skills/pws/SKILL.md +0 -0
  15. {bt_cli-0.4.50 → bt_cli-0.4.52}/.env.example +0 -0
  16. {bt_cli-0.4.50 → bt_cli-0.4.52}/.github/workflows/ci.yml +0 -0
  17. {bt_cli-0.4.50 → bt_cli-0.4.52}/.github/workflows/release.yml +0 -0
  18. {bt_cli-0.4.50 → bt_cli-0.4.52}/.gitignore +0 -0
  19. {bt_cli-0.4.50 → bt_cli-0.4.52}/README.md +0 -0
  20. {bt_cli-0.4.50 → bt_cli-0.4.52}/assets/cli-help.png +0 -0
  21. {bt_cli-0.4.50 → bt_cli-0.4.52}/assets/cli-output.png +0 -0
  22. {bt_cli-0.4.50 → bt_cli-0.4.52}/bt-cli.spec +0 -0
  23. {bt_cli-0.4.50 → bt_cli-0.4.52}/bt_entry.py +0 -0
  24. {bt_cli-0.4.50 → bt_cli-0.4.52}/epml-clients-server-side-filters-plan.md +0 -0
  25. {bt_cli-0.4.50 → bt_cli-0.4.52}/epml-implementation-plan.md +0 -0
  26. {bt_cli-0.4.50 → bt_cli-0.4.52}/pf-implementation-plan.md +0 -0
  27. {bt_cli-0.4.50 → bt_cli-0.4.52}/scripts/bt_entry.py +0 -0
  28. {bt_cli-0.4.50 → bt_cli-0.4.52}/scripts/pf_onboard.py +0 -0
  29. {bt_cli-0.4.50 → bt_cli-0.4.52}/scripts/sync-package-data.sh +0 -0
  30. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/cli.py +0 -0
  31. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/commands/__init__.py +0 -0
  32. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/commands/configure.py +0 -0
  33. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/commands/learn.py +0 -0
  34. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/commands/quick.py +0 -0
  35. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/core/__init__.py +0 -0
  36. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/core/auth.py +0 -0
  37. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/core/client.py +0 -0
  38. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/core/config.py +0 -0
  39. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/core/config_file.py +0 -0
  40. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/core/csv_utils.py +0 -0
  41. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/core/output.py +0 -0
  42. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/core/rest_debug.py +0 -0
  43. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/data/CLAUDE.md +0 -0
  44. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/data/__init__.py +0 -0
  45. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/data/skills/bt/SKILL.md +0 -0
  46. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/data/skills/entitle/SKILL.md +0 -0
  47. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/data/skills/epml/SKILL.md +0 -0
  48. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/data/skills/epmw/SKILL.md +0 -0
  49. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/data/skills/pra/SKILL.md +0 -0
  50. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/data/skills/pws/SKILL.md +0 -0
  51. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/data/skills/secrets/SKILL.md +0 -0
  52. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/entitle/__init__.py +0 -0
  53. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/entitle/client/__init__.py +0 -0
  54. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/entitle/client/base.py +0 -0
  55. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/entitle/commands/__init__.py +0 -0
  56. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/entitle/commands/accounts.py +0 -0
  57. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/entitle/commands/applications.py +0 -0
  58. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/entitle/commands/auth.py +0 -0
  59. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/entitle/commands/bundles.py +0 -0
  60. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/entitle/commands/integrations.py +0 -0
  61. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/entitle/commands/permissions.py +0 -0
  62. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/entitle/commands/policies.py +0 -0
  63. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/entitle/commands/requests.py +0 -0
  64. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/entitle/commands/resources.py +0 -0
  65. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/entitle/commands/roles.py +0 -0
  66. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/entitle/commands/users.py +0 -0
  67. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/entitle/commands/workflows.py +0 -0
  68. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/entitle/models/__init__.py +0 -0
  69. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/entitle/models/bundle.py +0 -0
  70. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/entitle/models/common.py +0 -0
  71. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/entitle/models/integration.py +0 -0
  72. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/entitle/models/permission.py +0 -0
  73. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/entitle/models/policy.py +0 -0
  74. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/entitle/models/resource.py +0 -0
  75. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/entitle/models/role.py +0 -0
  76. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/entitle/models/user.py +0 -0
  77. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/entitle/models/workflow.py +0 -0
  78. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/epml/__init__.py +0 -0
  79. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/epml/client/__init__.py +0 -0
  80. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/epml/client/base.py +0 -0
  81. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/epml/commands/__init__.py +0 -0
  82. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/epml/commands/audit.py +0 -0
  83. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/epml/commands/auth.py +0 -0
  84. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/epml/commands/client_pkg.py +0 -0
  85. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/epml/commands/clients.py +0 -0
  86. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/epml/commands/external_apis.py +0 -0
  87. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/epml/commands/hosts.py +0 -0
  88. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/epml/commands/iolog.py +0 -0
  89. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/epml/commands/license.py +0 -0
  90. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/epml/commands/quick.py +0 -0
  91. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/epml/commands/rbp_cmdgrps.py +0 -0
  92. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/epml/commands/rbp_entitlement.py +0 -0
  93. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/epml/commands/rbp_hostgrps.py +0 -0
  94. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/epml/commands/rbp_policy.py +0 -0
  95. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/epml/commands/rbp_roles.py +0 -0
  96. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/epml/commands/rbp_tests.py +0 -0
  97. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/epml/commands/rbp_tmdategrps.py +0 -0
  98. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/epml/commands/rbp_tx.py +0 -0
  99. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/epml/commands/rbp_usergrps.py +0 -0
  100. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/epml/commands/settings.py +0 -0
  101. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/epml/commands/siems.py +0 -0
  102. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/epml/commands/users.py +0 -0
  103. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/epml/models/__init__.py +0 -0
  104. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/epmw/__init__.py +0 -0
  105. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/epmw/client/__init__.py +0 -0
  106. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/epmw/client/base.py +0 -0
  107. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/epmw/commands/__init__.py +0 -0
  108. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/epmw/commands/audits.py +0 -0
  109. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/epmw/commands/auth.py +0 -0
  110. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/epmw/commands/computers.py +0 -0
  111. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/epmw/commands/events.py +0 -0
  112. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/epmw/commands/groups.py +0 -0
  113. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/epmw/commands/policies.py +0 -0
  114. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/epmw/commands/quick.py +0 -0
  115. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/epmw/commands/requests.py +0 -0
  116. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/epmw/commands/roles.py +0 -0
  117. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/epmw/commands/tasks.py +0 -0
  118. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/epmw/commands/users.py +0 -0
  119. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/epmw/models/__init__.py +0 -0
  120. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/pra/__init__.py +0 -0
  121. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/pra/client/__init__.py +0 -0
  122. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/pra/client/base.py +0 -0
  123. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/pra/commands/__init__.py +0 -0
  124. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/pra/commands/auth.py +0 -0
  125. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/pra/commands/group_policies.py +0 -0
  126. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/pra/commands/import_export.py +0 -0
  127. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/pra/commands/jump_clients.py +0 -0
  128. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/pra/commands/jump_groups.py +0 -0
  129. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/pra/commands/jump_items.py +0 -0
  130. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/pra/commands/jumpoints.py +0 -0
  131. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/pra/commands/policies.py +0 -0
  132. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/pra/commands/quick.py +0 -0
  133. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/pra/commands/teams.py +0 -0
  134. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/pra/commands/users.py +0 -0
  135. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/pra/commands/vault.py +0 -0
  136. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/pra/models/__init__.py +0 -0
  137. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/pra/models/common.py +0 -0
  138. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/pra/models/group_policy.py +0 -0
  139. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/pra/models/jump_client.py +0 -0
  140. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/pra/models/jump_group.py +0 -0
  141. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/pra/models/jump_item.py +0 -0
  142. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/pra/models/jumpoint.py +0 -0
  143. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/pra/models/team.py +0 -0
  144. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/pra/models/user.py +0 -0
  145. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/pra/models/vault.py +0 -0
  146. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/pws/__init__.py +0 -0
  147. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/pws/client/__init__.py +0 -0
  148. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/pws/client/base.py +0 -0
  149. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/pws/client/beyondinsight.py +0 -0
  150. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/pws/commands/__init__.py +0 -0
  151. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/pws/commands/assets.py +0 -0
  152. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/pws/commands/attributes.py +0 -0
  153. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/pws/commands/auth.py +0 -0
  154. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/pws/commands/clouds.py +0 -0
  155. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/pws/commands/config.py +0 -0
  156. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/pws/commands/credentials.py +0 -0
  157. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/pws/commands/databases.py +0 -0
  158. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/pws/commands/directories.py +0 -0
  159. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/pws/commands/functional.py +0 -0
  160. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/pws/commands/import_export.py +0 -0
  161. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/pws/commands/platforms.py +0 -0
  162. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/pws/commands/quick.py +0 -0
  163. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/pws/commands/search.py +0 -0
  164. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/pws/commands/secrets.py +0 -0
  165. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/pws/commands/systems.py +0 -0
  166. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/pws/commands/users.py +0 -0
  167. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/pws/commands/workgroups.py +0 -0
  168. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/pws/config.py +0 -0
  169. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/pws/models/__init__.py +0 -0
  170. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/pws/models/account.py +0 -0
  171. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/pws/models/asset.py +0 -0
  172. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/pws/models/common.py +0 -0
  173. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/pws/models/system.py +0 -0
  174. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/secrets/__init__.py +0 -0
  175. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/secrets/client/__init__.py +0 -0
  176. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/secrets/client/base.py +0 -0
  177. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/secrets/commands/__init__.py +0 -0
  178. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/secrets/commands/_hints.py +0 -0
  179. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/secrets/commands/auth.py +0 -0
  180. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/secrets/commands/dynamic.py +0 -0
  181. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/secrets/commands/folders.py +0 -0
  182. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/secrets/commands/integrations.py +0 -0
  183. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/secrets/commands/leases.py +0 -0
  184. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/secrets/commands/static.py +0 -0
  185. {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/secrets/models/__init__.py +0 -0
  186. {bt_cli-0.4.50 → bt_cli-0.4.52}/tests/__init__.py +0 -0
  187. {bt_cli-0.4.50 → bt_cli-0.4.52}/tests/conftest.py +0 -0
  188. {bt_cli-0.4.50 → bt_cli-0.4.52}/tests/core/__init__.py +0 -0
  189. {bt_cli-0.4.50 → bt_cli-0.4.52}/tests/core/test_auth.py +0 -0
  190. {bt_cli-0.4.50 → bt_cli-0.4.52}/tests/core/test_config.py +0 -0
  191. {bt_cli-0.4.50 → bt_cli-0.4.52}/tests/core/test_errors.py +0 -0
  192. {bt_cli-0.4.50 → bt_cli-0.4.52}/tests/core/test_rest_debug.py +0 -0
  193. {bt_cli-0.4.50 → bt_cli-0.4.52}/tests/entitle/__init__.py +0 -0
  194. {bt_cli-0.4.50 → bt_cli-0.4.52}/tests/entitle/test_client.py +0 -0
  195. {bt_cli-0.4.50 → bt_cli-0.4.52}/tests/entitle/test_commands.py +0 -0
  196. {bt_cli-0.4.50 → bt_cli-0.4.52}/tests/entitle-smoke-test.sh +0 -0
  197. {bt_cli-0.4.50 → bt_cli-0.4.52}/tests/epml/__init__.py +0 -0
  198. {bt_cli-0.4.50 → bt_cli-0.4.52}/tests/epml/test_client.py +0 -0
  199. {bt_cli-0.4.50 → bt_cli-0.4.52}/tests/epml/test_commands.py +0 -0
  200. {bt_cli-0.4.50 → bt_cli-0.4.52}/tests/epmw/__init__.py +0 -0
  201. {bt_cli-0.4.50 → bt_cli-0.4.52}/tests/epmw/test_client.py +0 -0
  202. {bt_cli-0.4.50 → bt_cli-0.4.52}/tests/epmw/test_commands.py +0 -0
  203. {bt_cli-0.4.50 → bt_cli-0.4.52}/tests/epmw-quick-test-plan.md +0 -0
  204. {bt_cli-0.4.50 → bt_cli-0.4.52}/tests/fixtures/__init__.py +0 -0
  205. {bt_cli-0.4.50 → bt_cli-0.4.52}/tests/fixtures/responses.py +0 -0
  206. {bt_cli-0.4.50 → bt_cli-0.4.52}/tests/integration/__init__.py +0 -0
  207. {bt_cli-0.4.50 → bt_cli-0.4.52}/tests/integration/conftest.py +0 -0
  208. {bt_cli-0.4.50 → bt_cli-0.4.52}/tests/integration/helpers.py +0 -0
  209. {bt_cli-0.4.50 → bt_cli-0.4.52}/tests/integration/test_entitle_integration.py +0 -0
  210. {bt_cli-0.4.50 → bt_cli-0.4.52}/tests/integration/test_epmw_integration.py +0 -0
  211. {bt_cli-0.4.50 → bt_cli-0.4.52}/tests/integration/test_epmw_lifecycle.py +0 -0
  212. {bt_cli-0.4.50 → bt_cli-0.4.52}/tests/integration/test_pra_integration.py +0 -0
  213. {bt_cli-0.4.50 → bt_cli-0.4.52}/tests/integration/test_pra_lifecycle.py +0 -0
  214. {bt_cli-0.4.50 → bt_cli-0.4.52}/tests/integration/test_pws_integration.py +0 -0
  215. {bt_cli-0.4.50 → bt_cli-0.4.52}/tests/integration/test_pws_lifecycle.py +0 -0
  216. {bt_cli-0.4.50 → bt_cli-0.4.52}/tests/pra/__init__.py +0 -0
  217. {bt_cli-0.4.50 → bt_cli-0.4.52}/tests/pra/test_client.py +0 -0
  218. {bt_cli-0.4.50 → bt_cli-0.4.52}/tests/pra/test_commands.py +0 -0
  219. {bt_cli-0.4.50 → bt_cli-0.4.52}/tests/pra-smoke-test.sh +0 -0
  220. {bt_cli-0.4.50 → bt_cli-0.4.52}/tests/pra-test-plan.md +0 -0
  221. {bt_cli-0.4.50 → bt_cli-0.4.52}/tests/pws/__init__.py +0 -0
  222. {bt_cli-0.4.50 → bt_cli-0.4.52}/tests/pws/test_client.py +0 -0
  223. {bt_cli-0.4.50 → bt_cli-0.4.52}/tests/pws/test_commands.py +0 -0
  224. {bt_cli-0.4.50 → bt_cli-0.4.52}/tests/pws-quick-test-plan.md +0 -0
  225. {bt_cli-0.4.50 → bt_cli-0.4.52}/tests/pws-smoke-test.sh +0 -0
  226. {bt_cli-0.4.50 → bt_cli-0.4.52}/tests/secrets/__init__.py +0 -0
  227. {bt_cli-0.4.50 → bt_cli-0.4.52}/tests/secrets/test_client.py +0 -0
  228. {bt_cli-0.4.50 → bt_cli-0.4.52}/tests/secrets/test_commands.py +0 -0
@@ -1,6 +1,6 @@
1
1
  # BT-CLI
2
2
 
3
- BeyondTrust Platform CLI for Password Safe, Entitle, PRA, EPM Windows, EPM Linux, and the BeyondTrust Secrets API. **Version: 0.4.50**
3
+ BeyondTrust Platform CLI for Password Safe, Entitle, PRA, EPM Windows, EPM Linux, and the BeyondTrust Secrets API. **Version: 0.4.52**
4
4
 
5
5
  ## Setup
6
6
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: bt-cli
3
- Version: 0.4.50
3
+ Version: 0.4.52
4
4
  Summary: BeyondTrust Platform CLI (unofficial) - Password Safe, Entitle, PRA, EPM
5
5
  Author-email: Dave Grendysz <dgrendysz@beyondtrust.com>
6
6
  License: MIT
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "bt-cli"
7
- version = "0.4.50"
7
+ version = "0.4.52"
8
8
  description = "BeyondTrust Platform CLI (unofficial) - Password Safe, Entitle, PRA, EPM"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.9"
@@ -1,3 +1,3 @@
1
1
  """BeyondTrust Unified Admin CLI."""
2
2
 
3
- __version__ = "0.4.50"
3
+ __version__ = "0.4.52"
@@ -165,6 +165,19 @@ def get_http_error_message(error: httpx.HTTPStatusError) -> str:
165
165
  # Response may not be JSON
166
166
  pass
167
167
 
168
+ # plain-text bodies (e.g. PWS 400 responses that aren't JSON)
169
+ if not detail:
170
+ try:
171
+ raw = error.response.text
172
+ if raw:
173
+ orig_len = len(raw)
174
+ sanitized = sanitize_error_message(raw[:300])
175
+ if sanitized:
176
+ detail = sanitized + ("..." if orig_len > 300 else "")
177
+ return f"{base_message}: {detail}"
178
+ except Exception:
179
+ pass
180
+
168
181
  if detail:
169
182
  # Additional regex sanitization for any patterns in the extracted detail
170
183
  detail = sanitize_error_message(str(detail))
@@ -1,16 +1,24 @@
1
1
  """Reusable interactive prompts for CLI commands."""
2
2
 
3
+ import re
3
4
  from typing import Any, Callable, Optional, TypeVar
4
5
 
5
6
  import typer
6
7
  from rich.console import Console
7
8
 
9
+ _ANSI_ESCAPE = re.compile(r"\x1b(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])")
10
+
8
11
  console = Console()
9
12
 
10
13
  T = TypeVar("T")
11
14
  D = TypeVar("D")
12
15
 
13
16
 
17
+ def _clean_str(raw: Any) -> str:
18
+ """Strip ANSI escape sequences from a prompted string value."""
19
+ return _ANSI_ESCAPE.sub("", raw).strip()
20
+
21
+
14
22
  def prompt_if_missing(
15
23
  value: Optional[T],
16
24
  prompt_text: str,
@@ -27,7 +35,10 @@ def prompt_if_missing(
27
35
  The value (either original or prompted)
28
36
  """
29
37
  if value is None or value == "":
30
- return typer.prompt(prompt_text, type=value_type)
38
+ raw = typer.prompt(prompt_text, type=value_type)
39
+ if value_type is str:
40
+ return _clean_str(raw) # type: ignore[return-value]
41
+ return raw
31
42
  return value
32
43
 
33
44
 
@@ -57,7 +68,10 @@ def prompt_from_list(
57
68
  item_id = item.get(id_key, "")
58
69
  item_name = item.get(name_key, "Unknown")
59
70
  console.print(f" {item_id}: {item_name}")
60
- return typer.prompt(prompt_text, type=value_type)
71
+ raw = typer.prompt(prompt_text, type=value_type)
72
+ if value_type is str:
73
+ return _clean_str(raw) # type: ignore[return-value]
74
+ return raw
61
75
 
62
76
 
63
77
  def prompt_choice(
@@ -80,6 +80,35 @@ class PasswordSafeMixin:
80
80
  """
81
81
  return self.get(f"/ManagedAccounts/{account_id}")
82
82
 
83
+ # =========================================================================
84
+ # Synced Accounts
85
+ # =========================================================================
86
+
87
+ def list_synced_accounts(
88
+ self: "PasswordSafeClient", account_id: int
89
+ ) -> list[dict[str, Any]]:
90
+ """List accounts synced to a managed account."""
91
+ result = self.get(f"/ManagedAccounts/{account_id}/SyncedAccounts")
92
+ return result if isinstance(result, list) else []
93
+
94
+ def add_synced_account(
95
+ self: "PasswordSafeClient", account_id: int, synced_account_id: int
96
+ ) -> dict[str, Any]:
97
+ """Add a synced account relationship."""
98
+ return self.post(f"/ManagedAccounts/{account_id}/SyncedAccounts/{synced_account_id}")
99
+
100
+ def remove_synced_account(
101
+ self: "PasswordSafeClient", account_id: int, synced_account_id: int
102
+ ) -> None:
103
+ """Remove a specific synced account."""
104
+ self.delete(f"/ManagedAccounts/{account_id}/SyncedAccounts/{synced_account_id}")
105
+
106
+ def remove_all_synced_accounts(
107
+ self: "PasswordSafeClient", account_id: int
108
+ ) -> None:
109
+ """Remove all synced accounts from a managed account."""
110
+ self.delete(f"/ManagedAccounts/{account_id}/SyncedAccounts")
111
+
83
112
  def get_managed_account_by_name(
84
113
  self: "PasswordSafeClient",
85
114
  system_name: str,
@@ -8,7 +8,7 @@ import typer
8
8
  from rich.console import Console
9
9
  from rich.table import Table
10
10
 
11
- from ...core.output import print_api_error
11
+ from ...core.output import print_api_error, print_success
12
12
  from ..client.base import get_client
13
13
 
14
14
  app = typer.Typer(no_args_is_help=True, help="""Manage managed accounts in Password Safe.
@@ -370,3 +370,168 @@ def rotate_password(
370
370
  except Exception as e:
371
371
  print_api_error(e, "manage accounts")
372
372
  raise typer.Exit(1)
373
+
374
+
375
+ # =============================================================================
376
+ # Synced Accounts
377
+ # =============================================================================
378
+
379
+ @app.command("synced-list")
380
+ def synced_list(
381
+ account_id: int = typer.Argument(..., help="Parent managed account ID"),
382
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table or json"),
383
+ ) -> None:
384
+ """List accounts synced to a managed account.
385
+
386
+ Examples:
387
+ bt pws accounts synced-list 14
388
+ bt pws accounts synced-list 14 -o json
389
+ """
390
+ try:
391
+ with get_client() as client:
392
+ client.authenticate()
393
+ accounts = client.list_synced_accounts(account_id)
394
+
395
+ if output == "json":
396
+ console.print_json(json.dumps(accounts, default=str))
397
+ elif accounts:
398
+ table = Table(title=f"Synced Accounts (parent ID: {account_id})")
399
+ table.add_column("Acct ID", style="cyan", no_wrap=True)
400
+ table.add_column("Account Name", style="green")
401
+ table.add_column("Domain", style="yellow")
402
+ table.add_column("Sys ID", style="blue", no_wrap=True)
403
+ table.add_column("Description", style="dim")
404
+ for a in accounts:
405
+ table.add_row(
406
+ str(a.get("ManagedAccountID", "")),
407
+ a.get("AccountName", ""),
408
+ a.get("DomainName") or "-",
409
+ str(a.get("ManagedSystemID", "")),
410
+ (a.get("Description") or "-")[:50],
411
+ )
412
+ console.print(table)
413
+ else:
414
+ console.print("[yellow]No synced accounts.[/yellow]")
415
+
416
+ except httpx.HTTPStatusError as e:
417
+ print_api_error(e, "synced-list")
418
+ raise typer.Exit(1)
419
+ except httpx.RequestError as e:
420
+ print_api_error(e, "synced-list")
421
+ raise typer.Exit(1)
422
+ except Exception as e:
423
+ print_api_error(e, "synced-list")
424
+ raise typer.Exit(1)
425
+
426
+
427
+ @app.command("synced-add")
428
+ def synced_add(
429
+ account_id: int = typer.Argument(..., help="Parent managed account ID"),
430
+ synced_account_id: int = typer.Argument(..., help="Account ID to sync"),
431
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table or json"),
432
+ ) -> None:
433
+ """Add a synced account relationship.
434
+
435
+ Synced accounts share password changes with the parent account.
436
+
437
+ Examples:
438
+ bt pws accounts synced-add 14 25
439
+ bt pws accounts synced-add 14 25 -o json
440
+ """
441
+ try:
442
+ with get_client() as client:
443
+ client.authenticate()
444
+ result = client.add_synced_account(account_id, synced_account_id)
445
+
446
+ if output == "json":
447
+ console.print_json(json.dumps(result, default=str))
448
+ else:
449
+ name = result.get("AccountName", str(synced_account_id))
450
+ print_success(f"Synced account '{name}' (ID: {synced_account_id}) added to account ID: {account_id}")
451
+
452
+ except httpx.HTTPStatusError as e:
453
+ print_api_error(e, "synced-add")
454
+ raise typer.Exit(1)
455
+ except httpx.RequestError as e:
456
+ print_api_error(e, "synced-add")
457
+ raise typer.Exit(1)
458
+ except Exception as e:
459
+ print_api_error(e, "synced-add")
460
+ raise typer.Exit(1)
461
+
462
+
463
+ @app.command("synced-remove")
464
+ def synced_remove(
465
+ account_id: int = typer.Argument(..., help="Parent managed account ID"),
466
+ synced_account_id: int = typer.Argument(..., help="Synced account ID to remove"),
467
+ force: bool = typer.Option(False, "--force", "-f", help="Skip confirmation"),
468
+ ) -> None:
469
+ """Remove a specific synced account.
470
+
471
+ Examples:
472
+ bt pws accounts synced-remove 14 25
473
+ bt pws accounts synced-remove 14 25 --force
474
+ """
475
+ try:
476
+ with get_client() as client:
477
+ client.authenticate()
478
+
479
+ if not force:
480
+ confirm = typer.confirm(f"Remove synced account {synced_account_id} from account {account_id}?")
481
+ if not confirm:
482
+ console.print("[yellow]Cancelled.[/yellow]")
483
+ raise typer.Exit(0)
484
+
485
+ client.remove_synced_account(account_id, synced_account_id)
486
+
487
+ print_success(f"Removed synced account ID: {synced_account_id} from account ID: {account_id}")
488
+
489
+ except httpx.HTTPStatusError as e:
490
+ print_api_error(e, "synced-remove")
491
+ raise typer.Exit(1)
492
+ except httpx.RequestError as e:
493
+ print_api_error(e, "synced-remove")
494
+ raise typer.Exit(1)
495
+ except typer.Exit:
496
+ raise
497
+ except Exception as e:
498
+ print_api_error(e, "synced-remove")
499
+ raise typer.Exit(1)
500
+
501
+
502
+ @app.command("synced-clear")
503
+ def synced_clear(
504
+ account_id: int = typer.Argument(..., help="Parent managed account ID"),
505
+ force: bool = typer.Option(False, "--force", "-f", help="Skip confirmation"),
506
+ ) -> None:
507
+ """Remove all synced accounts from a managed account.
508
+
509
+ Examples:
510
+ bt pws accounts synced-clear 14
511
+ bt pws accounts synced-clear 14 --force
512
+ """
513
+ try:
514
+ with get_client() as client:
515
+ client.authenticate()
516
+
517
+ if not force:
518
+ confirm = typer.confirm(f"Remove ALL synced accounts from account {account_id}?")
519
+ if not confirm:
520
+ console.print("[yellow]Cancelled.[/yellow]")
521
+ raise typer.Exit(0)
522
+
523
+ client.remove_all_synced_accounts(account_id)
524
+
525
+ print_success(f"All synced accounts removed from account ID: {account_id}")
526
+
527
+ except httpx.HTTPStatusError as e:
528
+ print_api_error(e, "synced-clear")
529
+ raise typer.Exit(1)
530
+ except httpx.RequestError as e:
531
+ print_api_error(e, "synced-clear")
532
+ raise typer.Exit(1)
533
+ except typer.Exit:
534
+ raise
535
+ except Exception as e:
536
+ print_api_error(e, "synced-clear")
537
+ raise typer.Exit(1)
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes