bt-cli 0.4.37__tar.gz → 0.4.38__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 (205) hide show
  1. {bt_cli-0.4.37/src/bt_cli/data → bt_cli-0.4.38/.claude}/skills/epml/SKILL.md +6 -1
  2. {bt_cli-0.4.37 → bt_cli-0.4.38}/CLAUDE.md +1 -1
  3. {bt_cli-0.4.37 → bt_cli-0.4.38}/PKG-INFO +1 -1
  4. {bt_cli-0.4.37 → bt_cli-0.4.38}/pyproject.toml +1 -1
  5. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/__init__.py +1 -1
  6. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/data/CLAUDE.md +1 -1
  7. {bt_cli-0.4.37/.claude → bt_cli-0.4.38/src/bt_cli/data}/skills/epml/SKILL.md +6 -1
  8. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/epml/client/base.py +23 -0
  9. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/epml/commands/rbp_roles.py +122 -6
  10. {bt_cli-0.4.37/src/bt_cli/data → bt_cli-0.4.38/.claude}/skills/bt/SKILL.md +0 -0
  11. {bt_cli-0.4.37/src/bt_cli/data → bt_cli-0.4.38/.claude}/skills/entitle/SKILL.md +0 -0
  12. {bt_cli-0.4.37/src/bt_cli/data → bt_cli-0.4.38/.claude}/skills/epmw/SKILL.md +0 -0
  13. {bt_cli-0.4.37/src/bt_cli/data → bt_cli-0.4.38/.claude}/skills/pra/SKILL.md +0 -0
  14. {bt_cli-0.4.37/src/bt_cli/data → bt_cli-0.4.38/.claude}/skills/pws/SKILL.md +0 -0
  15. {bt_cli-0.4.37 → bt_cli-0.4.38}/.env.example +0 -0
  16. {bt_cli-0.4.37 → bt_cli-0.4.38}/.github/workflows/ci.yml +0 -0
  17. {bt_cli-0.4.37 → bt_cli-0.4.38}/.github/workflows/release.yml +0 -0
  18. {bt_cli-0.4.37 → bt_cli-0.4.38}/.gitignore +0 -0
  19. {bt_cli-0.4.37 → bt_cli-0.4.38}/README.md +0 -0
  20. {bt_cli-0.4.37 → bt_cli-0.4.38}/assets/cli-help.png +0 -0
  21. {bt_cli-0.4.37 → bt_cli-0.4.38}/assets/cli-output.png +0 -0
  22. {bt_cli-0.4.37 → bt_cli-0.4.38}/bt-cli.spec +0 -0
  23. {bt_cli-0.4.37 → bt_cli-0.4.38}/bt_entry.py +0 -0
  24. {bt_cli-0.4.37 → bt_cli-0.4.38}/epml-implementation-plan.md +0 -0
  25. {bt_cli-0.4.37 → bt_cli-0.4.38}/scripts/bt_entry.py +0 -0
  26. {bt_cli-0.4.37 → bt_cli-0.4.38}/scripts/sync-package-data.sh +0 -0
  27. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/cli.py +0 -0
  28. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/commands/__init__.py +0 -0
  29. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/commands/configure.py +0 -0
  30. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/commands/learn.py +0 -0
  31. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/commands/quick.py +0 -0
  32. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/core/__init__.py +0 -0
  33. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/core/auth.py +0 -0
  34. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/core/client.py +0 -0
  35. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/core/config.py +0 -0
  36. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/core/config_file.py +0 -0
  37. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/core/csv_utils.py +0 -0
  38. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/core/errors.py +0 -0
  39. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/core/output.py +0 -0
  40. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/core/prompts.py +0 -0
  41. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/core/rest_debug.py +0 -0
  42. {bt_cli-0.4.37/tests/pws → bt_cli-0.4.38/src/bt_cli/data}/__init__.py +0 -0
  43. {bt_cli-0.4.37/.claude → bt_cli-0.4.38/src/bt_cli/data}/skills/bt/SKILL.md +0 -0
  44. {bt_cli-0.4.37/.claude → bt_cli-0.4.38/src/bt_cli/data}/skills/entitle/SKILL.md +0 -0
  45. {bt_cli-0.4.37/.claude → bt_cli-0.4.38/src/bt_cli/data}/skills/epmw/SKILL.md +0 -0
  46. {bt_cli-0.4.37/.claude → bt_cli-0.4.38/src/bt_cli/data}/skills/pra/SKILL.md +0 -0
  47. {bt_cli-0.4.37/.claude → bt_cli-0.4.38/src/bt_cli/data}/skills/pws/SKILL.md +0 -0
  48. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/entitle/__init__.py +0 -0
  49. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/entitle/client/__init__.py +0 -0
  50. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/entitle/client/base.py +0 -0
  51. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/entitle/commands/__init__.py +0 -0
  52. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/entitle/commands/accounts.py +0 -0
  53. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/entitle/commands/applications.py +0 -0
  54. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/entitle/commands/auth.py +0 -0
  55. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/entitle/commands/bundles.py +0 -0
  56. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/entitle/commands/integrations.py +0 -0
  57. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/entitle/commands/permissions.py +0 -0
  58. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/entitle/commands/policies.py +0 -0
  59. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/entitle/commands/resources.py +0 -0
  60. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/entitle/commands/roles.py +0 -0
  61. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/entitle/commands/users.py +0 -0
  62. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/entitle/commands/workflows.py +0 -0
  63. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/entitle/models/__init__.py +0 -0
  64. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/entitle/models/bundle.py +0 -0
  65. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/entitle/models/common.py +0 -0
  66. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/entitle/models/integration.py +0 -0
  67. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/entitle/models/permission.py +0 -0
  68. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/entitle/models/policy.py +0 -0
  69. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/entitle/models/resource.py +0 -0
  70. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/entitle/models/role.py +0 -0
  71. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/entitle/models/user.py +0 -0
  72. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/entitle/models/workflow.py +0 -0
  73. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/epml/__init__.py +0 -0
  74. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/epml/client/__init__.py +0 -0
  75. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/epml/commands/__init__.py +0 -0
  76. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/epml/commands/audit.py +0 -0
  77. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/epml/commands/auth.py +0 -0
  78. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/epml/commands/client_pkg.py +0 -0
  79. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/epml/commands/external_apis.py +0 -0
  80. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/epml/commands/hosts.py +0 -0
  81. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/epml/commands/iolog.py +0 -0
  82. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/epml/commands/license.py +0 -0
  83. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/epml/commands/quick.py +0 -0
  84. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/epml/commands/rbp_cmdgrps.py +0 -0
  85. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/epml/commands/rbp_entitlement.py +0 -0
  86. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/epml/commands/rbp_hostgrps.py +0 -0
  87. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/epml/commands/rbp_policy.py +0 -0
  88. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/epml/commands/rbp_tests.py +0 -0
  89. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/epml/commands/rbp_tmdategrps.py +0 -0
  90. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/epml/commands/rbp_tx.py +0 -0
  91. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/epml/commands/rbp_usergrps.py +0 -0
  92. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/epml/commands/settings.py +0 -0
  93. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/epml/commands/siems.py +0 -0
  94. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/epml/commands/users.py +0 -0
  95. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/epml/models/__init__.py +0 -0
  96. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/epmw/__init__.py +0 -0
  97. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/epmw/client/__init__.py +0 -0
  98. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/epmw/client/base.py +0 -0
  99. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/epmw/commands/__init__.py +0 -0
  100. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/epmw/commands/audits.py +0 -0
  101. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/epmw/commands/auth.py +0 -0
  102. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/epmw/commands/computers.py +0 -0
  103. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/epmw/commands/events.py +0 -0
  104. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/epmw/commands/groups.py +0 -0
  105. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/epmw/commands/policies.py +0 -0
  106. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/epmw/commands/quick.py +0 -0
  107. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/epmw/commands/requests.py +0 -0
  108. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/epmw/commands/roles.py +0 -0
  109. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/epmw/commands/tasks.py +0 -0
  110. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/epmw/commands/users.py +0 -0
  111. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/epmw/models/__init__.py +0 -0
  112. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/pra/__init__.py +0 -0
  113. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/pra/client/__init__.py +0 -0
  114. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/pra/client/base.py +0 -0
  115. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/pra/commands/__init__.py +0 -0
  116. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/pra/commands/auth.py +0 -0
  117. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/pra/commands/import_export.py +0 -0
  118. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/pra/commands/jump_clients.py +0 -0
  119. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/pra/commands/jump_groups.py +0 -0
  120. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/pra/commands/jump_items.py +0 -0
  121. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/pra/commands/jumpoints.py +0 -0
  122. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/pra/commands/policies.py +0 -0
  123. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/pra/commands/quick.py +0 -0
  124. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/pra/commands/teams.py +0 -0
  125. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/pra/commands/users.py +0 -0
  126. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/pra/commands/vault.py +0 -0
  127. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/pra/models/__init__.py +0 -0
  128. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/pra/models/common.py +0 -0
  129. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/pra/models/jump_client.py +0 -0
  130. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/pra/models/jump_group.py +0 -0
  131. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/pra/models/jump_item.py +0 -0
  132. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/pra/models/jumpoint.py +0 -0
  133. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/pra/models/team.py +0 -0
  134. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/pra/models/user.py +0 -0
  135. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/pra/models/vault.py +0 -0
  136. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/pws/__init__.py +0 -0
  137. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/pws/client/__init__.py +0 -0
  138. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/pws/client/base.py +0 -0
  139. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/pws/client/beyondinsight.py +0 -0
  140. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/pws/client/passwordsafe.py +0 -0
  141. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/pws/commands/__init__.py +0 -0
  142. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/pws/commands/accounts.py +0 -0
  143. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/pws/commands/assets.py +0 -0
  144. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/pws/commands/attributes.py +0 -0
  145. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/pws/commands/auth.py +0 -0
  146. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/pws/commands/clouds.py +0 -0
  147. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/pws/commands/config.py +0 -0
  148. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/pws/commands/credentials.py +0 -0
  149. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/pws/commands/databases.py +0 -0
  150. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/pws/commands/directories.py +0 -0
  151. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/pws/commands/functional.py +0 -0
  152. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/pws/commands/import_export.py +0 -0
  153. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/pws/commands/platforms.py +0 -0
  154. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/pws/commands/quick.py +0 -0
  155. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/pws/commands/search.py +0 -0
  156. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/pws/commands/secrets.py +0 -0
  157. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/pws/commands/systems.py +0 -0
  158. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/pws/commands/users.py +0 -0
  159. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/pws/commands/workgroups.py +0 -0
  160. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/pws/config.py +0 -0
  161. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/pws/models/__init__.py +0 -0
  162. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/pws/models/account.py +0 -0
  163. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/pws/models/asset.py +0 -0
  164. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/pws/models/common.py +0 -0
  165. {bt_cli-0.4.37 → bt_cli-0.4.38}/src/bt_cli/pws/models/system.py +0 -0
  166. {bt_cli-0.4.37 → bt_cli-0.4.38}/tests/__init__.py +0 -0
  167. {bt_cli-0.4.37 → bt_cli-0.4.38}/tests/conftest.py +0 -0
  168. {bt_cli-0.4.37 → bt_cli-0.4.38}/tests/core/__init__.py +0 -0
  169. {bt_cli-0.4.37 → bt_cli-0.4.38}/tests/core/test_auth.py +0 -0
  170. {bt_cli-0.4.37 → bt_cli-0.4.38}/tests/core/test_config.py +0 -0
  171. {bt_cli-0.4.37 → bt_cli-0.4.38}/tests/core/test_errors.py +0 -0
  172. {bt_cli-0.4.37 → bt_cli-0.4.38}/tests/core/test_rest_debug.py +0 -0
  173. {bt_cli-0.4.37 → bt_cli-0.4.38}/tests/entitle/__init__.py +0 -0
  174. {bt_cli-0.4.37 → bt_cli-0.4.38}/tests/entitle/test_client.py +0 -0
  175. {bt_cli-0.4.37 → bt_cli-0.4.38}/tests/entitle/test_commands.py +0 -0
  176. {bt_cli-0.4.37 → bt_cli-0.4.38}/tests/entitle-smoke-test.sh +0 -0
  177. {bt_cli-0.4.37 → bt_cli-0.4.38}/tests/epml/__init__.py +0 -0
  178. {bt_cli-0.4.37 → bt_cli-0.4.38}/tests/epml/test_client.py +0 -0
  179. {bt_cli-0.4.37 → bt_cli-0.4.38}/tests/epml/test_commands.py +0 -0
  180. {bt_cli-0.4.37 → bt_cli-0.4.38}/tests/epmw/__init__.py +0 -0
  181. {bt_cli-0.4.37 → bt_cli-0.4.38}/tests/epmw/test_client.py +0 -0
  182. {bt_cli-0.4.37 → bt_cli-0.4.38}/tests/epmw/test_commands.py +0 -0
  183. {bt_cli-0.4.37 → bt_cli-0.4.38}/tests/epmw-quick-test-plan.md +0 -0
  184. {bt_cli-0.4.37 → bt_cli-0.4.38}/tests/fixtures/__init__.py +0 -0
  185. {bt_cli-0.4.37 → bt_cli-0.4.38}/tests/fixtures/responses.py +0 -0
  186. {bt_cli-0.4.37 → bt_cli-0.4.38}/tests/integration/__init__.py +0 -0
  187. {bt_cli-0.4.37 → bt_cli-0.4.38}/tests/integration/conftest.py +0 -0
  188. {bt_cli-0.4.37 → bt_cli-0.4.38}/tests/integration/helpers.py +0 -0
  189. {bt_cli-0.4.37 → bt_cli-0.4.38}/tests/integration/test_entitle_integration.py +0 -0
  190. {bt_cli-0.4.37 → bt_cli-0.4.38}/tests/integration/test_epmw_integration.py +0 -0
  191. {bt_cli-0.4.37 → bt_cli-0.4.38}/tests/integration/test_epmw_lifecycle.py +0 -0
  192. {bt_cli-0.4.37 → bt_cli-0.4.38}/tests/integration/test_pra_integration.py +0 -0
  193. {bt_cli-0.4.37 → bt_cli-0.4.38}/tests/integration/test_pra_lifecycle.py +0 -0
  194. {bt_cli-0.4.37 → bt_cli-0.4.38}/tests/integration/test_pws_integration.py +0 -0
  195. {bt_cli-0.4.37 → bt_cli-0.4.38}/tests/integration/test_pws_lifecycle.py +0 -0
  196. {bt_cli-0.4.37 → bt_cli-0.4.38}/tests/pra/__init__.py +0 -0
  197. {bt_cli-0.4.37 → bt_cli-0.4.38}/tests/pra/test_client.py +0 -0
  198. {bt_cli-0.4.37 → bt_cli-0.4.38}/tests/pra/test_commands.py +0 -0
  199. {bt_cli-0.4.37 → bt_cli-0.4.38}/tests/pra-smoke-test.sh +0 -0
  200. {bt_cli-0.4.37 → bt_cli-0.4.38}/tests/pra-test-plan.md +0 -0
  201. {bt_cli-0.4.37/src/bt_cli/data → bt_cli-0.4.38/tests/pws}/__init__.py +0 -0
  202. {bt_cli-0.4.37 → bt_cli-0.4.38}/tests/pws/test_client.py +0 -0
  203. {bt_cli-0.4.37 → bt_cli-0.4.38}/tests/pws/test_commands.py +0 -0
  204. {bt_cli-0.4.37 → bt_cli-0.4.38}/tests/pws-quick-test-plan.md +0 -0
  205. {bt_cli-0.4.37 → bt_cli-0.4.38}/tests/pws-smoke-test.sh +0 -0
@@ -133,8 +133,13 @@ bt epml rbp tmdategrps tmdates replace <tmdategrp_id> --file tmdates.json
133
133
  # Roles + assignments
134
134
  bt epml rbp roles list
135
135
  bt epml rbp roles create --name "Helpdesk Role" --action A \
136
+ --description "..." --comment "..." --tag "AdminAccess" \
137
+ --risk 6 --rpt 1 \
136
138
  --iolog '/iologs/%date%/%uniqueid%.iolog' \
137
- --message "This session is logged."
139
+ --banner-text "Helpdesk Role" # builds standard ###-framed banner
140
+ # or --message "raw multi-line message"
141
+ # or --banner # banner with %rbprole% template
142
+ bt epml rbp roles update <id> --tag NewTag --risk 9 --rpt 1 # in-place edit
138
143
  bt epml rbp roles duplicate <role_id>
139
144
  bt epml rbp roles cmdgrps add <role_id> --ids 1,2 # cmdgrps & tmdategrps: just IDs
140
145
  bt epml rbp roles tmdategrps add <role_id> --ids 1
@@ -1,6 +1,6 @@
1
1
  # BT-CLI
2
2
 
3
- BeyondTrust Platform CLI for Password Safe, Entitle, PRA, EPM Windows, and EPM Linux. **Version: 0.4.37**
3
+ BeyondTrust Platform CLI for Password Safe, Entitle, PRA, EPM Windows, and EPM Linux. **Version: 0.4.38**
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.37
3
+ Version: 0.4.38
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.37"
7
+ version = "0.4.38"
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.37"
3
+ __version__ = "0.4.38"
@@ -1,6 +1,6 @@
1
1
  # BT-CLI
2
2
 
3
- BeyondTrust Platform CLI for Password Safe, Entitle, PRA, EPM Windows, and EPM Linux. **Version: 0.4.37**
3
+ BeyondTrust Platform CLI for Password Safe, Entitle, PRA, EPM Windows, and EPM Linux. **Version: 0.4.38**
4
4
 
5
5
  ## Setup
6
6
 
@@ -133,8 +133,13 @@ bt epml rbp tmdategrps tmdates replace <tmdategrp_id> --file tmdates.json
133
133
  # Roles + assignments
134
134
  bt epml rbp roles list
135
135
  bt epml rbp roles create --name "Helpdesk Role" --action A \
136
+ --description "..." --comment "..." --tag "AdminAccess" \
137
+ --risk 6 --rpt 1 \
136
138
  --iolog '/iologs/%date%/%uniqueid%.iolog' \
137
- --message "This session is logged."
139
+ --banner-text "Helpdesk Role" # builds standard ###-framed banner
140
+ # or --message "raw multi-line message"
141
+ # or --banner # banner with %rbprole% template
142
+ bt epml rbp roles update <id> --tag NewTag --risk 9 --rpt 1 # in-place edit
138
143
  bt epml rbp roles duplicate <role_id>
139
144
  bt epml rbp roles cmdgrps add <role_id> --ids 1,2 # cmdgrps & tmdategrps: just IDs
140
145
  bt epml rbp roles tmdategrps add <role_id> --ids 1
@@ -476,6 +476,29 @@ class EPMLClient:
476
476
  body.setdefault("action", "A")
477
477
  return self.post(f"/api/pbul/{h}/rbp/roles", json=body)
478
478
 
479
+ def update_role(self, role_id: int, partial: Dict[str, Any], host_id: Optional[int] = None) -> Any:
480
+ """Update an existing role (read-modify-write).
481
+
482
+ The API's create/update endpoint OVERWRITES on `id` match — fields not
483
+ in the body get zeroed/cleared. To make an update feel like a partial
484
+ modification, this helper fetches the current role, merges in your
485
+ changes, and posts the merged record. Avoids accidentally clobbering
486
+ `action`, `name`, `iolog`, etc. when you only meant to change `tag`.
487
+
488
+ Caveat: the v1 API has no GET-single-role, so we list and filter.
489
+ """
490
+ h = self.host(host_id)
491
+ roles = self.list_roles(host_id=host_id) or []
492
+ current = next((r for r in roles if r.get("id") == role_id), None)
493
+ if current is None:
494
+ raise ValueError(f"role id {role_id} not found")
495
+ # role child relations (rolecmds/roleusers/etc.) get filtered out so we
496
+ # don't accidentally rewrite assignments — those have their own endpoints.
497
+ merged = {k: v for k, v in current.items() if not k.startswith("role")}
498
+ merged.update(partial)
499
+ merged["id"] = role_id
500
+ return self.post(f"/api/pbul/{h}/rbp/roles", json=merged)
501
+
479
502
  def delete_roles(self, ids: List[int], host_id: Optional[int] = None) -> None:
480
503
  h = self.host(host_id)
481
504
  for rid in ids:
@@ -14,6 +14,37 @@ def _host_opt():
14
14
  return typer.Option(None, "--host", "-H", help="PMUL host id (default: BT_EPML_DEFAULT_HOST)")
15
15
 
16
16
 
17
+ _BAR = "#" * 60
18
+
19
+
20
+ def _build_banner(title: Optional[str] = None) -> str:
21
+ """Build a banner-style policy message.
22
+
23
+ Server substitutes `%rbprole%` and `%event%` at session time. Pass a
24
+ title string to replace the role-name line with literal text.
25
+ """
26
+ title_line = f" Policy: {title}\r\n" if title else " Policy: %rbprole%\r\n"
27
+ return (
28
+ "\r\n"
29
+ + _BAR + "\r\n"
30
+ + title_line
31
+ + " Status: %event%\r\n"
32
+ + " Session Recorded: Yes\r\n"
33
+ + _BAR + "\r\n"
34
+ )
35
+
36
+
37
+ def _resolve_message(message: Optional[str], banner: bool, banner_text: Optional[str]) -> Optional[str]:
38
+ """Pick the final message: explicit --message wins; else build a banner if asked."""
39
+ if message is not None:
40
+ return message
41
+ if banner_text is not None:
42
+ return _build_banner(banner_text)
43
+ if banner:
44
+ return _build_banner()
45
+ return None # leave message unset
46
+
47
+
17
48
  @app.command("list")
18
49
  def list_roles(
19
50
  host: Optional[int] = _host_opt(),
@@ -45,13 +76,18 @@ def list_roles(
45
76
  def create_role(
46
77
  name: str = typer.Option(..., "--name", "-n"),
47
78
  description: str = typer.Option("", "--description", "-d"),
79
+ comment: Optional[str] = typer.Option(None, "--comment", "-c", help="Internal comment (defaults to description if omitted)"),
80
+ tag: Optional[str] = typer.Option(None, "--tag", help="Free-form tag for filtering/reporting"),
81
+ risk: Optional[int] = typer.Option(None, "--risk", "-r", help="Risk score 0-9 (Postgres-style example uses 6)"),
82
+ rpt: Optional[int] = typer.Option(None, "--rpt", help="Entitlement reporting: 1 to surface in `entitlement run`, 0 to hide"),
48
83
  action: str = typer.Option("A", "--action", "-a", help="Role verdict: A=Allow, R=Reject"),
49
84
  iolog: Optional[str] = typer.Option(
50
85
  None, "--iolog",
51
86
  help="I/O log path template (e.g. /iologs/%date%/%uniqueid%.iolog). Omit to disable.",
52
87
  ),
53
- message: Optional[str] = typer.Option(None, "--message", "-m", help="Message shown to the requesting user"),
54
- comment: Optional[str] = typer.Option(None, "--comment", help="Internal comment"),
88
+ message: Optional[str] = typer.Option(None, "--message", "-m", help="Raw message shown to the requesting user (multi-line OK; verbatim)"),
89
+ banner: bool = typer.Option(False, "--banner", help="Use a standard ###-framed banner with %rbprole% and %event% template variables"),
90
+ banner_text: Optional[str] = typer.Option(None, "--banner-text", help="Like --banner but use the given title text instead of %rbprole% substitution"),
55
91
  disabled: bool = typer.Option(False, "--disabled", help="Create the role disabled"),
56
92
  host: Optional[int] = _host_opt(),
57
93
  ):
@@ -62,18 +98,42 @@ def create_role(
62
98
 
63
99
  `iolog` accepts the appliance's path template syntax — typical value is
64
100
  `/iologs/%date%/%uniqueid%.iolog`. Omit to disable I/O logging on this role.
101
+
102
+ Three ways to set the user-visible message (in priority order):
103
+ --message "raw text" verbatim
104
+ --banner-text "Custom Title" builds the standard banner with this title
105
+ --banner builds the standard banner using %rbprole%
106
+
107
+ The standard banner format mirrors the appliance's existing roles:
108
+ ##############################
109
+ Policy: <title or %rbprole%>
110
+ Status: %event%
111
+ Session Recorded: Yes
112
+ ##############################
65
113
  """
66
114
  from bt_cli.epml.client import get_client
67
115
  if action not in ("A", "R"):
68
116
  typer.echo(f"--action must be 'A' or 'R', got {action!r}", err=True)
69
117
  raise typer.Exit(2)
118
+
70
119
  body = {"name": name, "description": description, "action": action, "disabled": disabled}
71
- if iolog is not None:
72
- body["iolog"] = iolog
73
- if message is not None:
74
- body["message"] = message
75
120
  if comment is not None:
76
121
  body["comment"] = comment
122
+ elif description:
123
+ body["comment"] = description # mirror description into comment (matches existing roles)
124
+ if tag is not None:
125
+ body["tag"] = tag
126
+ if risk is not None:
127
+ body["risk"] = risk
128
+ if rpt is not None:
129
+ body["rpt"] = rpt
130
+ if iolog is not None:
131
+ body["iolog"] = iolog
132
+
133
+ final_message = _resolve_message(message, banner, banner_text)
134
+ if final_message is not None:
135
+ body["message"] = final_message
136
+
77
137
  try:
78
138
  with get_client() as c:
79
139
  result = c.create_role(body, host_id=host)
@@ -84,6 +144,62 @@ def create_role(
84
144
  print_api_error(e, "create role"); raise typer.Exit(1)
85
145
 
86
146
 
147
+ @app.command("update")
148
+ def update_role(
149
+ role_id: int = typer.Argument(..., help="Role ID to update"),
150
+ name: Optional[str] = typer.Option(None, "--name", "-n", help="Rename the role"),
151
+ description: Optional[str] = typer.Option(None, "--description", "-d"),
152
+ comment: Optional[str] = typer.Option(None, "--comment", "-c"),
153
+ tag: Optional[str] = typer.Option(None, "--tag"),
154
+ risk: Optional[int] = typer.Option(None, "--risk", "-r"),
155
+ rpt: Optional[int] = typer.Option(None, "--rpt"),
156
+ action: Optional[str] = typer.Option(None, "--action", "-a", help="A=Allow / R=Reject"),
157
+ iolog: Optional[str] = typer.Option(None, "--iolog", help="I/O log template; pass '' to disable"),
158
+ message: Optional[str] = typer.Option(None, "--message", "-m"),
159
+ banner: bool = typer.Option(False, "--banner"),
160
+ banner_text: Optional[str] = typer.Option(None, "--banner-text"),
161
+ disabled: Optional[bool] = typer.Option(None, "--disabled/--enabled"),
162
+ host: Optional[int] = _host_opt(),
163
+ ):
164
+ """Update a role's metadata in place. Only fields you pass are modified.
165
+
166
+ Examples:
167
+ bt epml rbp roles update 126 --tag "RootAccess" --risk 9 --rpt 1
168
+ bt epml rbp roles update 126 --banner-text "Root Shell Access"
169
+ bt epml rbp roles update 126 --message "Custom session header..."
170
+ """
171
+ from bt_cli.epml.client import get_client
172
+ if action is not None and action not in ("A", "R"):
173
+ typer.echo(f"--action must be 'A' or 'R', got {action!r}", err=True)
174
+ raise typer.Exit(2)
175
+
176
+ body: dict = {}
177
+ for key, val in (
178
+ ("name", name), ("description", description), ("comment", comment),
179
+ ("tag", tag), ("risk", risk), ("rpt", rpt), ("action", action),
180
+ ("iolog", iolog), ("disabled", disabled),
181
+ ):
182
+ if val is not None:
183
+ body[key] = val
184
+
185
+ final_message = _resolve_message(message, banner, banner_text)
186
+ if final_message is not None:
187
+ body["message"] = final_message
188
+
189
+ if not body:
190
+ typer.echo("Nothing to update — pass at least one field flag.", err=True)
191
+ raise typer.Exit(2)
192
+
193
+ try:
194
+ with get_client() as c:
195
+ result = c.update_role(role_id, body, host_id=host)
196
+ print_json(result)
197
+ except httpx.HTTPStatusError as e:
198
+ print_api_error(e, "update role"); raise typer.Exit(1)
199
+ except Exception as e:
200
+ print_api_error(e, "update role"); raise typer.Exit(1)
201
+
202
+
87
203
  @app.command("delete")
88
204
  def delete_role(
89
205
  ids: str = typer.Argument(..., help="Comma-separated role IDs"),
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
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