bt-cli 0.4.36__tar.gz → 0.4.37__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.36/src/bt_cli/data → bt_cli-0.4.37/.claude}/skills/epml/SKILL.md +11 -4
  2. {bt_cli-0.4.36 → bt_cli-0.4.37}/CLAUDE.md +1 -1
  3. {bt_cli-0.4.36 → bt_cli-0.4.37}/PKG-INFO +1 -1
  4. {bt_cli-0.4.36 → bt_cli-0.4.37}/pyproject.toml +1 -1
  5. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/__init__.py +1 -1
  6. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/data/CLAUDE.md +1 -1
  7. {bt_cli-0.4.36/.claude → bt_cli-0.4.37/src/bt_cli/data}/skills/epml/SKILL.md +11 -4
  8. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/epml/client/base.py +54 -8
  9. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/epml/commands/rbp_roles.py +77 -24
  10. {bt_cli-0.4.36/src/bt_cli/data → bt_cli-0.4.37/.claude}/skills/bt/SKILL.md +0 -0
  11. {bt_cli-0.4.36/src/bt_cli/data → bt_cli-0.4.37/.claude}/skills/entitle/SKILL.md +0 -0
  12. {bt_cli-0.4.36/src/bt_cli/data → bt_cli-0.4.37/.claude}/skills/epmw/SKILL.md +0 -0
  13. {bt_cli-0.4.36/src/bt_cli/data → bt_cli-0.4.37/.claude}/skills/pra/SKILL.md +0 -0
  14. {bt_cli-0.4.36/src/bt_cli/data → bt_cli-0.4.37/.claude}/skills/pws/SKILL.md +0 -0
  15. {bt_cli-0.4.36 → bt_cli-0.4.37}/.env.example +0 -0
  16. {bt_cli-0.4.36 → bt_cli-0.4.37}/.github/workflows/ci.yml +0 -0
  17. {bt_cli-0.4.36 → bt_cli-0.4.37}/.github/workflows/release.yml +0 -0
  18. {bt_cli-0.4.36 → bt_cli-0.4.37}/.gitignore +0 -0
  19. {bt_cli-0.4.36 → bt_cli-0.4.37}/README.md +0 -0
  20. {bt_cli-0.4.36 → bt_cli-0.4.37}/assets/cli-help.png +0 -0
  21. {bt_cli-0.4.36 → bt_cli-0.4.37}/assets/cli-output.png +0 -0
  22. {bt_cli-0.4.36 → bt_cli-0.4.37}/bt-cli.spec +0 -0
  23. {bt_cli-0.4.36 → bt_cli-0.4.37}/bt_entry.py +0 -0
  24. {bt_cli-0.4.36 → bt_cli-0.4.37}/epml-implementation-plan.md +0 -0
  25. {bt_cli-0.4.36 → bt_cli-0.4.37}/scripts/bt_entry.py +0 -0
  26. {bt_cli-0.4.36 → bt_cli-0.4.37}/scripts/sync-package-data.sh +0 -0
  27. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/cli.py +0 -0
  28. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/commands/__init__.py +0 -0
  29. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/commands/configure.py +0 -0
  30. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/commands/learn.py +0 -0
  31. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/commands/quick.py +0 -0
  32. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/core/__init__.py +0 -0
  33. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/core/auth.py +0 -0
  34. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/core/client.py +0 -0
  35. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/core/config.py +0 -0
  36. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/core/config_file.py +0 -0
  37. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/core/csv_utils.py +0 -0
  38. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/core/errors.py +0 -0
  39. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/core/output.py +0 -0
  40. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/core/prompts.py +0 -0
  41. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/core/rest_debug.py +0 -0
  42. {bt_cli-0.4.36/tests/pws → bt_cli-0.4.37/src/bt_cli/data}/__init__.py +0 -0
  43. {bt_cli-0.4.36/.claude → bt_cli-0.4.37/src/bt_cli/data}/skills/bt/SKILL.md +0 -0
  44. {bt_cli-0.4.36/.claude → bt_cli-0.4.37/src/bt_cli/data}/skills/entitle/SKILL.md +0 -0
  45. {bt_cli-0.4.36/.claude → bt_cli-0.4.37/src/bt_cli/data}/skills/epmw/SKILL.md +0 -0
  46. {bt_cli-0.4.36/.claude → bt_cli-0.4.37/src/bt_cli/data}/skills/pra/SKILL.md +0 -0
  47. {bt_cli-0.4.36/.claude → bt_cli-0.4.37/src/bt_cli/data}/skills/pws/SKILL.md +0 -0
  48. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/entitle/__init__.py +0 -0
  49. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/entitle/client/__init__.py +0 -0
  50. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/entitle/client/base.py +0 -0
  51. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/entitle/commands/__init__.py +0 -0
  52. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/entitle/commands/accounts.py +0 -0
  53. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/entitle/commands/applications.py +0 -0
  54. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/entitle/commands/auth.py +0 -0
  55. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/entitle/commands/bundles.py +0 -0
  56. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/entitle/commands/integrations.py +0 -0
  57. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/entitle/commands/permissions.py +0 -0
  58. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/entitle/commands/policies.py +0 -0
  59. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/entitle/commands/resources.py +0 -0
  60. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/entitle/commands/roles.py +0 -0
  61. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/entitle/commands/users.py +0 -0
  62. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/entitle/commands/workflows.py +0 -0
  63. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/entitle/models/__init__.py +0 -0
  64. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/entitle/models/bundle.py +0 -0
  65. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/entitle/models/common.py +0 -0
  66. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/entitle/models/integration.py +0 -0
  67. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/entitle/models/permission.py +0 -0
  68. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/entitle/models/policy.py +0 -0
  69. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/entitle/models/resource.py +0 -0
  70. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/entitle/models/role.py +0 -0
  71. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/entitle/models/user.py +0 -0
  72. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/entitle/models/workflow.py +0 -0
  73. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/epml/__init__.py +0 -0
  74. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/epml/client/__init__.py +0 -0
  75. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/epml/commands/__init__.py +0 -0
  76. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/epml/commands/audit.py +0 -0
  77. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/epml/commands/auth.py +0 -0
  78. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/epml/commands/client_pkg.py +0 -0
  79. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/epml/commands/external_apis.py +0 -0
  80. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/epml/commands/hosts.py +0 -0
  81. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/epml/commands/iolog.py +0 -0
  82. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/epml/commands/license.py +0 -0
  83. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/epml/commands/quick.py +0 -0
  84. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/epml/commands/rbp_cmdgrps.py +0 -0
  85. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/epml/commands/rbp_entitlement.py +0 -0
  86. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/epml/commands/rbp_hostgrps.py +0 -0
  87. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/epml/commands/rbp_policy.py +0 -0
  88. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/epml/commands/rbp_tests.py +0 -0
  89. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/epml/commands/rbp_tmdategrps.py +0 -0
  90. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/epml/commands/rbp_tx.py +0 -0
  91. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/epml/commands/rbp_usergrps.py +0 -0
  92. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/epml/commands/settings.py +0 -0
  93. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/epml/commands/siems.py +0 -0
  94. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/epml/commands/users.py +0 -0
  95. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/epml/models/__init__.py +0 -0
  96. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/epmw/__init__.py +0 -0
  97. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/epmw/client/__init__.py +0 -0
  98. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/epmw/client/base.py +0 -0
  99. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/epmw/commands/__init__.py +0 -0
  100. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/epmw/commands/audits.py +0 -0
  101. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/epmw/commands/auth.py +0 -0
  102. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/epmw/commands/computers.py +0 -0
  103. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/epmw/commands/events.py +0 -0
  104. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/epmw/commands/groups.py +0 -0
  105. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/epmw/commands/policies.py +0 -0
  106. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/epmw/commands/quick.py +0 -0
  107. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/epmw/commands/requests.py +0 -0
  108. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/epmw/commands/roles.py +0 -0
  109. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/epmw/commands/tasks.py +0 -0
  110. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/epmw/commands/users.py +0 -0
  111. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/epmw/models/__init__.py +0 -0
  112. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/pra/__init__.py +0 -0
  113. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/pra/client/__init__.py +0 -0
  114. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/pra/client/base.py +0 -0
  115. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/pra/commands/__init__.py +0 -0
  116. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/pra/commands/auth.py +0 -0
  117. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/pra/commands/import_export.py +0 -0
  118. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/pra/commands/jump_clients.py +0 -0
  119. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/pra/commands/jump_groups.py +0 -0
  120. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/pra/commands/jump_items.py +0 -0
  121. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/pra/commands/jumpoints.py +0 -0
  122. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/pra/commands/policies.py +0 -0
  123. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/pra/commands/quick.py +0 -0
  124. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/pra/commands/teams.py +0 -0
  125. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/pra/commands/users.py +0 -0
  126. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/pra/commands/vault.py +0 -0
  127. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/pra/models/__init__.py +0 -0
  128. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/pra/models/common.py +0 -0
  129. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/pra/models/jump_client.py +0 -0
  130. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/pra/models/jump_group.py +0 -0
  131. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/pra/models/jump_item.py +0 -0
  132. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/pra/models/jumpoint.py +0 -0
  133. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/pra/models/team.py +0 -0
  134. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/pra/models/user.py +0 -0
  135. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/pra/models/vault.py +0 -0
  136. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/pws/__init__.py +0 -0
  137. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/pws/client/__init__.py +0 -0
  138. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/pws/client/base.py +0 -0
  139. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/pws/client/beyondinsight.py +0 -0
  140. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/pws/client/passwordsafe.py +0 -0
  141. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/pws/commands/__init__.py +0 -0
  142. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/pws/commands/accounts.py +0 -0
  143. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/pws/commands/assets.py +0 -0
  144. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/pws/commands/attributes.py +0 -0
  145. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/pws/commands/auth.py +0 -0
  146. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/pws/commands/clouds.py +0 -0
  147. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/pws/commands/config.py +0 -0
  148. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/pws/commands/credentials.py +0 -0
  149. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/pws/commands/databases.py +0 -0
  150. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/pws/commands/directories.py +0 -0
  151. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/pws/commands/functional.py +0 -0
  152. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/pws/commands/import_export.py +0 -0
  153. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/pws/commands/platforms.py +0 -0
  154. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/pws/commands/quick.py +0 -0
  155. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/pws/commands/search.py +0 -0
  156. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/pws/commands/secrets.py +0 -0
  157. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/pws/commands/systems.py +0 -0
  158. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/pws/commands/users.py +0 -0
  159. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/pws/commands/workgroups.py +0 -0
  160. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/pws/config.py +0 -0
  161. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/pws/models/__init__.py +0 -0
  162. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/pws/models/account.py +0 -0
  163. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/pws/models/asset.py +0 -0
  164. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/pws/models/common.py +0 -0
  165. {bt_cli-0.4.36 → bt_cli-0.4.37}/src/bt_cli/pws/models/system.py +0 -0
  166. {bt_cli-0.4.36 → bt_cli-0.4.37}/tests/__init__.py +0 -0
  167. {bt_cli-0.4.36 → bt_cli-0.4.37}/tests/conftest.py +0 -0
  168. {bt_cli-0.4.36 → bt_cli-0.4.37}/tests/core/__init__.py +0 -0
  169. {bt_cli-0.4.36 → bt_cli-0.4.37}/tests/core/test_auth.py +0 -0
  170. {bt_cli-0.4.36 → bt_cli-0.4.37}/tests/core/test_config.py +0 -0
  171. {bt_cli-0.4.36 → bt_cli-0.4.37}/tests/core/test_errors.py +0 -0
  172. {bt_cli-0.4.36 → bt_cli-0.4.37}/tests/core/test_rest_debug.py +0 -0
  173. {bt_cli-0.4.36 → bt_cli-0.4.37}/tests/entitle/__init__.py +0 -0
  174. {bt_cli-0.4.36 → bt_cli-0.4.37}/tests/entitle/test_client.py +0 -0
  175. {bt_cli-0.4.36 → bt_cli-0.4.37}/tests/entitle/test_commands.py +0 -0
  176. {bt_cli-0.4.36 → bt_cli-0.4.37}/tests/entitle-smoke-test.sh +0 -0
  177. {bt_cli-0.4.36 → bt_cli-0.4.37}/tests/epml/__init__.py +0 -0
  178. {bt_cli-0.4.36 → bt_cli-0.4.37}/tests/epml/test_client.py +0 -0
  179. {bt_cli-0.4.36 → bt_cli-0.4.37}/tests/epml/test_commands.py +0 -0
  180. {bt_cli-0.4.36 → bt_cli-0.4.37}/tests/epmw/__init__.py +0 -0
  181. {bt_cli-0.4.36 → bt_cli-0.4.37}/tests/epmw/test_client.py +0 -0
  182. {bt_cli-0.4.36 → bt_cli-0.4.37}/tests/epmw/test_commands.py +0 -0
  183. {bt_cli-0.4.36 → bt_cli-0.4.37}/tests/epmw-quick-test-plan.md +0 -0
  184. {bt_cli-0.4.36 → bt_cli-0.4.37}/tests/fixtures/__init__.py +0 -0
  185. {bt_cli-0.4.36 → bt_cli-0.4.37}/tests/fixtures/responses.py +0 -0
  186. {bt_cli-0.4.36 → bt_cli-0.4.37}/tests/integration/__init__.py +0 -0
  187. {bt_cli-0.4.36 → bt_cli-0.4.37}/tests/integration/conftest.py +0 -0
  188. {bt_cli-0.4.36 → bt_cli-0.4.37}/tests/integration/helpers.py +0 -0
  189. {bt_cli-0.4.36 → bt_cli-0.4.37}/tests/integration/test_entitle_integration.py +0 -0
  190. {bt_cli-0.4.36 → bt_cli-0.4.37}/tests/integration/test_epmw_integration.py +0 -0
  191. {bt_cli-0.4.36 → bt_cli-0.4.37}/tests/integration/test_epmw_lifecycle.py +0 -0
  192. {bt_cli-0.4.36 → bt_cli-0.4.37}/tests/integration/test_pra_integration.py +0 -0
  193. {bt_cli-0.4.36 → bt_cli-0.4.37}/tests/integration/test_pra_lifecycle.py +0 -0
  194. {bt_cli-0.4.36 → bt_cli-0.4.37}/tests/integration/test_pws_integration.py +0 -0
  195. {bt_cli-0.4.36 → bt_cli-0.4.37}/tests/integration/test_pws_lifecycle.py +0 -0
  196. {bt_cli-0.4.36 → bt_cli-0.4.37}/tests/pra/__init__.py +0 -0
  197. {bt_cli-0.4.36 → bt_cli-0.4.37}/tests/pra/test_client.py +0 -0
  198. {bt_cli-0.4.36 → bt_cli-0.4.37}/tests/pra/test_commands.py +0 -0
  199. {bt_cli-0.4.36 → bt_cli-0.4.37}/tests/pra-smoke-test.sh +0 -0
  200. {bt_cli-0.4.36 → bt_cli-0.4.37}/tests/pra-test-plan.md +0 -0
  201. {bt_cli-0.4.36/src/bt_cli/data → bt_cli-0.4.37/tests/pws}/__init__.py +0 -0
  202. {bt_cli-0.4.36 → bt_cli-0.4.37}/tests/pws/test_client.py +0 -0
  203. {bt_cli-0.4.36 → bt_cli-0.4.37}/tests/pws/test_commands.py +0 -0
  204. {bt_cli-0.4.36 → bt_cli-0.4.37}/tests/pws-quick-test-plan.md +0 -0
  205. {bt_cli-0.4.36 → bt_cli-0.4.37}/tests/pws-smoke-test.sh +0 -0
@@ -132,13 +132,17 @@ bt epml rbp tmdategrps tmdates replace <tmdategrp_id> --file tmdates.json
132
132
 
133
133
  # Roles + assignments
134
134
  bt epml rbp roles list
135
- bt epml rbp roles create --name "Helpdesk Role"
135
+ bt epml rbp roles create --name "Helpdesk Role" --action A \
136
+ --iolog '/iologs/%date%/%uniqueid%.iolog' \
137
+ --message "This session is logged."
136
138
  bt epml rbp roles duplicate <role_id>
137
- bt epml rbp roles cmdgrps add <role_id> --ids 1,2
139
+ bt epml rbp roles cmdgrps add <role_id> --ids 1,2 # cmdgrps & tmdategrps: just IDs
140
+ bt epml rbp roles tmdategrps add <role_id> --ids 1
141
+ bt epml rbp roles hostgrps add <role_id> --ids 1 --kind B # B = both Submit and Run-as
142
+ bt epml rbp roles usergrps add <role_id> --ids 4 --kind S # S = Submit (who requests)
143
+ bt epml rbp roles usergrps add <role_id> --ids 3 --kind R # R = Run-as (whose identity)
138
144
  bt epml rbp roles cmdgrps remove <role_id> <cmdgrp_id>
139
145
  bt epml rbp roles hostgrps list <role_id>
140
- bt epml rbp roles usergrps add <role_id> --ids 5
141
- bt epml rbp roles tmdategrps add <role_id> --ids 1
142
146
 
143
147
  # Entitlement report ('who can do what')
144
148
  bt epml rbp entitlement run
@@ -200,6 +204,9 @@ bt epml quick tests-then-deploy --suite <suite_id> # commits if pass; rollb
200
204
  - **`POST /usergrps/multiple`** (bulk create): body is `{"usergroups": [...]}` — undocumented wrapper key.
201
205
  - **POST on child collections is additive**, not replacing. Calling `commands add` twice with the same command will get you a duplicate.
202
206
  - **Children share the parent's `id`** in GET responses — the listed `id` field is the cmdgrp/hostgrp/usergrp ID, not unique per child. The actual identifier is the `cmd`/`host`/`user` text.
207
+ - **Role assignments take a single object per request, not an array**. The CLI loops under the hood; the wire body is e.g. `{"cmds": 35}`, `{"hosts": 1, "type": "S"}`, `{"users": 4, "type": "R"}`, `{"tmdates": 1}`.
208
+ - **Hostgrp / usergrp assignments require a `type` field** (`S` = Submit / who requests, `R` = Run-as / whose identity the command runs under). Without `type` the request 400s with "RBP role type not in: [S,R]". CLI: `--kind S|R|B` on `roles hostgrps add` and `roles usergrps add`. `B` (default) creates both an S row and an R row for the same id — appropriate when the same group plays both roles. For "Admin requests, runs as root", do `--ids 4 --kind S` and `--ids 3 --kind R` separately.
209
+ - **Roles need `rpt: 1`** to appear in `bt epml rbp entitlement run`. The role still functions for policy evaluation either way, but only `rpt=1` roles are surfaced in the report. Not yet exposed by the CLI — file-level edit via export/import is the workaround.
203
210
 
204
211
  ## Known gaps (TODO)
205
212
 
@@ -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.36**
3
+ BeyondTrust Platform CLI for Password Safe, Entitle, PRA, EPM Windows, and EPM Linux. **Version: 0.4.37**
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.36
3
+ Version: 0.4.37
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.36"
7
+ version = "0.4.37"
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.36"
3
+ __version__ = "0.4.37"
@@ -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.36**
3
+ BeyondTrust Platform CLI for Password Safe, Entitle, PRA, EPM Windows, and EPM Linux. **Version: 0.4.37**
4
4
 
5
5
  ## Setup
6
6
 
@@ -132,13 +132,17 @@ bt epml rbp tmdategrps tmdates replace <tmdategrp_id> --file tmdates.json
132
132
 
133
133
  # Roles + assignments
134
134
  bt epml rbp roles list
135
- bt epml rbp roles create --name "Helpdesk Role"
135
+ bt epml rbp roles create --name "Helpdesk Role" --action A \
136
+ --iolog '/iologs/%date%/%uniqueid%.iolog' \
137
+ --message "This session is logged."
136
138
  bt epml rbp roles duplicate <role_id>
137
- bt epml rbp roles cmdgrps add <role_id> --ids 1,2
139
+ bt epml rbp roles cmdgrps add <role_id> --ids 1,2 # cmdgrps & tmdategrps: just IDs
140
+ bt epml rbp roles tmdategrps add <role_id> --ids 1
141
+ bt epml rbp roles hostgrps add <role_id> --ids 1 --kind B # B = both Submit and Run-as
142
+ bt epml rbp roles usergrps add <role_id> --ids 4 --kind S # S = Submit (who requests)
143
+ bt epml rbp roles usergrps add <role_id> --ids 3 --kind R # R = Run-as (whose identity)
138
144
  bt epml rbp roles cmdgrps remove <role_id> <cmdgrp_id>
139
145
  bt epml rbp roles hostgrps list <role_id>
140
- bt epml rbp roles usergrps add <role_id> --ids 5
141
- bt epml rbp roles tmdategrps add <role_id> --ids 1
142
146
 
143
147
  # Entitlement report ('who can do what')
144
148
  bt epml rbp entitlement run
@@ -200,6 +204,9 @@ bt epml quick tests-then-deploy --suite <suite_id> # commits if pass; rollb
200
204
  - **`POST /usergrps/multiple`** (bulk create): body is `{"usergroups": [...]}` — undocumented wrapper key.
201
205
  - **POST on child collections is additive**, not replacing. Calling `commands add` twice with the same command will get you a duplicate.
202
206
  - **Children share the parent's `id`** in GET responses — the listed `id` field is the cmdgrp/hostgrp/usergrp ID, not unique per child. The actual identifier is the `cmd`/`host`/`user` text.
207
+ - **Role assignments take a single object per request, not an array**. The CLI loops under the hood; the wire body is e.g. `{"cmds": 35}`, `{"hosts": 1, "type": "S"}`, `{"users": 4, "type": "R"}`, `{"tmdates": 1}`.
208
+ - **Hostgrp / usergrp assignments require a `type` field** (`S` = Submit / who requests, `R` = Run-as / whose identity the command runs under). Without `type` the request 400s with "RBP role type not in: [S,R]". CLI: `--kind S|R|B` on `roles hostgrps add` and `roles usergrps add`. `B` (default) creates both an S row and an R row for the same id — appropriate when the same group plays both roles. For "Admin requests, runs as root", do `--ids 4 --kind S` and `--ids 3 --kind R` separately.
209
+ - **Roles need `rpt: 1`** to appear in `bt epml rbp entitlement run`. The role still functions for policy evaluation either way, but only `rpt=1` roles are surfaced in the report. Not yet exposed by the CLI — file-level edit via export/import is the workaround.
203
210
 
204
211
  ## Known gaps (TODO)
205
212
 
@@ -489,9 +489,13 @@ class EPMLClient:
489
489
  h = self.host(host_id)
490
490
  return self.get(f"/api/pbul/{h}/rbp/roles/{role_id}/cmdgrps")
491
491
 
492
- def add_role_cmdgrps(self, role_id: int, cmdgrp_ids: List[int], host_id: Optional[int] = None) -> Any:
492
+ def add_role_cmdgrps(self, role_id: int, cmdgrp_ids: List[int], host_id: Optional[int] = None) -> List[Any]:
493
+ """Add cmdgrps to a role. The API accepts ONE assignment per request
494
+ as a bare object (`{cmds: <id>}`) — not an array. Loop here.
495
+ """
493
496
  h = self.host(host_id)
494
- return self.post(f"/api/pbul/{h}/rbp/roles/{role_id}/cmdgrps", json=cmdgrp_ids)
497
+ path = f"/api/pbul/{h}/rbp/roles/{role_id}/cmdgrps"
498
+ return [self.post(path, json={"cmds": cid}) for cid in cmdgrp_ids]
495
499
 
496
500
  def remove_role_cmdgrp(self, role_id: int, cmdgrp_id: int, host_id: Optional[int] = None) -> None:
497
501
  h = self.host(host_id)
@@ -501,9 +505,31 @@ class EPMLClient:
501
505
  h = self.host(host_id)
502
506
  return self.get(f"/api/pbul/{h}/rbp/roles/{role_id}/hostgrps")
503
507
 
504
- def add_role_hostgrps(self, role_id: int, hostgrp_ids: List[int], host_id: Optional[int] = None) -> Any:
508
+ def add_role_hostgrps(
509
+ self,
510
+ role_id: int,
511
+ hostgrp_ids: List[int],
512
+ kind: str = "B",
513
+ host_id: Optional[int] = None,
514
+ ) -> List[Any]:
515
+ """Add hostgrps to a role. Each assignment carries a `type`:
516
+ S = Submit (where the request comes from)
517
+ R = Run-as (where the command actually runs)
518
+
519
+ For a typical "users on these hosts can run on these same hosts" rule,
520
+ you usually want BOTH — pass `kind="B"` (default) and the client posts
521
+ twice, once with type S and once with type R.
522
+ """
505
523
  h = self.host(host_id)
506
- return self.post(f"/api/pbul/{h}/rbp/roles/{role_id}/hostgrps", json=hostgrp_ids)
524
+ path = f"/api/pbul/{h}/rbp/roles/{role_id}/hostgrps"
525
+ types = ("S", "R") if kind.upper() == "B" else (kind.upper(),)
526
+ if not all(t in ("S", "R") for t in types):
527
+ raise ValueError(f"hostgrp kind must be S, R, or B (both); got {kind!r}")
528
+ results = []
529
+ for hid in hostgrp_ids:
530
+ for t in types:
531
+ results.append(self.post(path, json={"hosts": hid, "type": t}))
532
+ return results
507
533
 
508
534
  def remove_role_hostgrp(self, role_id: int, hostgrp_id: int, host_id: Optional[int] = None) -> None:
509
535
  h = self.host(host_id)
@@ -513,9 +539,27 @@ class EPMLClient:
513
539
  h = self.host(host_id)
514
540
  return self.get(f"/api/pbul/{h}/rbp/roles/{role_id}/usergrps")
515
541
 
516
- def add_role_usergrps(self, role_id: int, usergrp_ids: List[int], host_id: Optional[int] = None) -> Any:
542
+ def add_role_usergrps(
543
+ self,
544
+ role_id: int,
545
+ usergrp_ids: List[int],
546
+ kind: str = "B",
547
+ host_id: Optional[int] = None,
548
+ ) -> List[Any]:
549
+ """Add usergrps to a role. Same S/R/B `type` semantics as hostgrps.
550
+ S = Submit user (who requests)
551
+ R = Run-as user (whose identity the command runs under)
552
+ """
517
553
  h = self.host(host_id)
518
- return self.post(f"/api/pbul/{h}/rbp/roles/{role_id}/usergrps", json=usergrp_ids)
554
+ path = f"/api/pbul/{h}/rbp/roles/{role_id}/usergrps"
555
+ types = ("S", "R") if kind.upper() == "B" else (kind.upper(),)
556
+ if not all(t in ("S", "R") for t in types):
557
+ raise ValueError(f"usergrp kind must be S, R, or B (both); got {kind!r}")
558
+ results = []
559
+ for uid in usergrp_ids:
560
+ for t in types:
561
+ results.append(self.post(path, json={"users": uid, "type": t}))
562
+ return results
519
563
 
520
564
  def remove_role_usergrp(self, role_id: int, usergrp_id: int, host_id: Optional[int] = None) -> None:
521
565
  h = self.host(host_id)
@@ -525,9 +569,11 @@ class EPMLClient:
525
569
  h = self.host(host_id)
526
570
  return self.get(f"/api/pbul/{h}/rbp/roles/{role_id}/tmdategrps")
527
571
 
528
- def add_role_tmdategrps(self, role_id: int, tmdategrp_ids: List[int], host_id: Optional[int] = None) -> Any:
572
+ def add_role_tmdategrps(self, role_id: int, tmdategrp_ids: List[int], host_id: Optional[int] = None) -> List[Any]:
573
+ """Add tmdategrps to a role. Single object per request, key is `tmdates`."""
529
574
  h = self.host(host_id)
530
- return self.post(f"/api/pbul/{h}/rbp/roles/{role_id}/tmdategrps", json=tmdategrp_ids)
575
+ path = f"/api/pbul/{h}/rbp/roles/{role_id}/tmdategrps"
576
+ return [self.post(path, json={"tmdates": tid}) for tid in tmdategrp_ids]
531
577
 
532
578
  def remove_role_tmdategrp(self, role_id: int, tmdategrp_id: int, host_id: Optional[int] = None) -> None:
533
579
  h = self.host(host_id)
@@ -46,20 +46,37 @@ def create_role(
46
46
  name: str = typer.Option(..., "--name", "-n"),
47
47
  description: str = typer.Option("", "--description", "-d"),
48
48
  action: str = typer.Option("A", "--action", "-a", help="Role verdict: A=Allow, R=Reject"),
49
+ iolog: Optional[str] = typer.Option(
50
+ None, "--iolog",
51
+ help="I/O log path template (e.g. /iologs/%date%/%uniqueid%.iolog). Omit to disable.",
52
+ ),
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"),
55
+ disabled: bool = typer.Option(False, "--disabled", help="Create the role disabled"),
49
56
  host: Optional[int] = _host_opt(),
50
57
  ):
51
58
  """Create a role.
52
59
 
53
60
  The API requires `action` to be exactly `A` (Allow) or `R` (Reject) —
54
61
  not 'Allow'/'Reject'. Defaults to A.
62
+
63
+ `iolog` accepts the appliance's path template syntax — typical value is
64
+ `/iologs/%date%/%uniqueid%.iolog`. Omit to disable I/O logging on this role.
55
65
  """
56
66
  from bt_cli.epml.client import get_client
57
67
  if action not in ("A", "R"):
58
68
  typer.echo(f"--action must be 'A' or 'R', got {action!r}", err=True)
59
69
  raise typer.Exit(2)
70
+ 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
+ if comment is not None:
76
+ body["comment"] = comment
60
77
  try:
61
78
  with get_client() as c:
62
- result = c.create_role({"name": name, "description": description, "action": action}, host_id=host)
79
+ result = c.create_role(body, host_id=host)
63
80
  print_json(result)
64
81
  except httpx.HTTPStatusError as e:
65
82
  print_api_error(e, "create role"); raise typer.Exit(1)
@@ -104,15 +121,20 @@ def duplicate_role(
104
121
 
105
122
  # ---- assignments: cmdgrps / hostgrps / usergrps / tmdategrps ----
106
123
 
107
- def _make_assignment_app(label: str, list_fn: str, add_fn: str, remove_fn: str):
108
- """Build a sub-typer for managing one role-child resource type."""
124
+ def _make_assignment_app(label: str, list_fn: str, add_fn: str, remove_fn: str, has_kind: bool = False):
125
+ """Build a sub-typer for managing one role-child resource type.
126
+
127
+ has_kind: if True, expose --kind S|R|B (Submit / Run-as / Both). Used for
128
+ hostgrps and usergrps where each assignment carries a type. Cmdgrps and
129
+ tmdategrps don't take a kind.
130
+ """
109
131
  sub = typer.Typer(no_args_is_help=True, help=f"Manage {label} on a role")
110
132
 
111
133
  @sub.command("list")
112
134
  def _list(
113
135
  role_id: int = typer.Argument(..., help="Role ID"),
114
136
  host: Optional[int] = _host_opt(),
115
- output: OutputFormat = typer.Option(OutputFormat.TABLE, "--output", "-o"),
137
+ output: OutputFormat = typer.Option(OutputFormat.JSON, "--output", "-o"),
116
138
  ):
117
139
  f"""List {label} assigned to a role."""
118
140
  from bt_cli.epml.client import get_client
@@ -123,29 +145,60 @@ def _make_assignment_app(label: str, list_fn: str, add_fn: str, remove_fn: str):
123
145
  print_json(data)
124
146
  else:
125
147
  rows = data if isinstance(data, list) else (data.get("data", []) if isinstance(data, dict) else [])
126
- print_table(rows, [("ID", "id"), ("Name", "name")], title=f"{label} on role {role_id}")
148
+ # Best-effort table: include `type` column when present
149
+ if rows and isinstance(rows[0], dict) and "type" in rows[0]:
150
+ cols = [(k.upper(), k) for k in rows[0].keys()]
151
+ else:
152
+ cols = [("ID", "id"), ("Name", "name")]
153
+ print_table(rows, cols, title=f"{label} on role {role_id}")
127
154
  except httpx.HTTPStatusError as e:
128
155
  print_api_error(e, f"list role {label}"); raise typer.Exit(1)
129
156
  except Exception as e:
130
157
  print_api_error(e, f"list role {label}"); raise typer.Exit(1)
131
158
 
132
- @sub.command("add")
133
- def _add(
134
- role_id: int = typer.Argument(..., help="Role ID"),
135
- ids: str = typer.Option(..., "--ids", help=f"Comma-separated {label} IDs to add"),
136
- host: Optional[int] = _host_opt(),
137
- ):
138
- f"""Add {label} to a role."""
139
- from bt_cli.epml.client import get_client
140
- try:
141
- id_list = [int(x.strip()) for x in ids.split(",") if x.strip()]
142
- with get_client() as c:
143
- result = getattr(c, add_fn)(role_id, id_list, host_id=host)
144
- print_json(result)
145
- except httpx.HTTPStatusError as e:
146
- print_api_error(e, f"add role {label}"); raise typer.Exit(1)
147
- except Exception as e:
148
- print_api_error(e, f"add role {label}"); raise typer.Exit(1)
159
+ if has_kind:
160
+ @sub.command("add")
161
+ def _add(
162
+ role_id: int = typer.Argument(..., help="Role ID"),
163
+ ids: str = typer.Option(..., "--ids", help=f"Comma-separated {label} IDs to add"),
164
+ kind: str = typer.Option(
165
+ "B", "--kind", "-k",
166
+ help="Assignment type: S=Submit, R=Run-as, B=Both (creates two assignments per id)",
167
+ ),
168
+ host: Optional[int] = _host_opt(),
169
+ ):
170
+ f"""Add {label} to a role."""
171
+ from bt_cli.epml.client import get_client
172
+ if kind.upper() not in ("S", "R", "B"):
173
+ typer.echo(f"--kind must be S, R, or B; got {kind!r}", err=True)
174
+ raise typer.Exit(2)
175
+ try:
176
+ id_list = [int(x.strip()) for x in ids.split(",") if x.strip()]
177
+ with get_client() as c:
178
+ result = getattr(c, add_fn)(role_id, id_list, kind=kind.upper(), host_id=host)
179
+ print_json(result)
180
+ except httpx.HTTPStatusError as e:
181
+ print_api_error(e, f"add role {label}"); raise typer.Exit(1)
182
+ except Exception as e:
183
+ print_api_error(e, f"add role {label}"); raise typer.Exit(1)
184
+ else:
185
+ @sub.command("add")
186
+ def _add(
187
+ role_id: int = typer.Argument(..., help="Role ID"),
188
+ ids: str = typer.Option(..., "--ids", help=f"Comma-separated {label} IDs to add"),
189
+ host: Optional[int] = _host_opt(),
190
+ ):
191
+ f"""Add {label} to a role."""
192
+ from bt_cli.epml.client import get_client
193
+ try:
194
+ id_list = [int(x.strip()) for x in ids.split(",") if x.strip()]
195
+ with get_client() as c:
196
+ result = getattr(c, add_fn)(role_id, id_list, host_id=host)
197
+ print_json(result)
198
+ except httpx.HTTPStatusError as e:
199
+ print_api_error(e, f"add role {label}"); raise typer.Exit(1)
200
+ except Exception as e:
201
+ print_api_error(e, f"add role {label}"); raise typer.Exit(1)
149
202
 
150
203
  @sub.command("remove")
151
204
  def _remove(
@@ -168,6 +221,6 @@ def _make_assignment_app(label: str, list_fn: str, add_fn: str, remove_fn: str):
168
221
 
169
222
 
170
223
  app.add_typer(_make_assignment_app("cmdgrps", "list_role_cmdgrps", "add_role_cmdgrps", "remove_role_cmdgrp"), name="cmdgrps")
171
- app.add_typer(_make_assignment_app("hostgrps", "list_role_hostgrps", "add_role_hostgrps", "remove_role_hostgrp"), name="hostgrps")
172
- app.add_typer(_make_assignment_app("usergrps", "list_role_usergrps", "add_role_usergrps", "remove_role_usergrp"), name="usergrps")
224
+ app.add_typer(_make_assignment_app("hostgrps", "list_role_hostgrps", "add_role_hostgrps", "remove_role_hostgrp", has_kind=True), name="hostgrps")
225
+ app.add_typer(_make_assignment_app("usergrps", "list_role_usergrps", "add_role_usergrps", "remove_role_usergrp", has_kind=True), name="usergrps")
173
226
  app.add_typer(_make_assignment_app("tmdategrps", "list_role_tmdategrps", "add_role_tmdategrps", "remove_role_tmdategrp"), name="tmdategrps")
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