bt-cli 0.4.45__tar.gz → 0.4.47__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.45 → bt_cli-0.4.47}/CLAUDE.md +8 -2
  2. {bt_cli-0.4.45 → bt_cli-0.4.47}/PKG-INFO +30 -1
  3. {bt_cli-0.4.45 → bt_cli-0.4.47}/README.md +29 -0
  4. {bt_cli-0.4.45 → bt_cli-0.4.47}/pyproject.toml +1 -1
  5. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/__init__.py +1 -1
  6. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/cli.py +86 -2
  7. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/core/config.py +69 -1
  8. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/core/config_file.py +35 -0
  9. bt_cli-0.4.47/src/bt_cli/data/skills/secrets/SKILL.md +212 -0
  10. bt_cli-0.4.47/src/bt_cli/secrets/__init__.py +1 -0
  11. bt_cli-0.4.47/src/bt_cli/secrets/client/__init__.py +5 -0
  12. bt_cli-0.4.47/src/bt_cli/secrets/client/base.py +404 -0
  13. bt_cli-0.4.47/src/bt_cli/secrets/commands/__init__.py +23 -0
  14. bt_cli-0.4.47/src/bt_cli/secrets/commands/_hints.py +30 -0
  15. bt_cli-0.4.47/src/bt_cli/secrets/commands/auth.py +67 -0
  16. bt_cli-0.4.47/src/bt_cli/secrets/commands/dynamic.py +301 -0
  17. bt_cli-0.4.47/src/bt_cli/secrets/commands/folders.py +159 -0
  18. bt_cli-0.4.47/src/bt_cli/secrets/commands/integrations.py +201 -0
  19. bt_cli-0.4.47/src/bt_cli/secrets/commands/leases.py +122 -0
  20. bt_cli-0.4.47/src/bt_cli/secrets/commands/static.py +257 -0
  21. bt_cli-0.4.47/src/bt_cli/secrets/models/__init__.py +1 -0
  22. bt_cli-0.4.47/tests/secrets/__init__.py +1 -0
  23. bt_cli-0.4.47/tests/secrets/test_client.py +263 -0
  24. bt_cli-0.4.47/tests/secrets/test_commands.py +334 -0
  25. {bt_cli-0.4.45 → bt_cli-0.4.47}/.claude/skills/bt/SKILL.md +0 -0
  26. {bt_cli-0.4.45 → bt_cli-0.4.47}/.claude/skills/entitle/SKILL.md +0 -0
  27. {bt_cli-0.4.45 → bt_cli-0.4.47}/.claude/skills/epml/SKILL.md +0 -0
  28. {bt_cli-0.4.45 → bt_cli-0.4.47}/.claude/skills/epmw/SKILL.md +0 -0
  29. {bt_cli-0.4.45 → bt_cli-0.4.47}/.claude/skills/pra/SKILL.md +0 -0
  30. {bt_cli-0.4.45 → bt_cli-0.4.47}/.claude/skills/pws/SKILL.md +0 -0
  31. {bt_cli-0.4.45 → bt_cli-0.4.47}/.env.example +0 -0
  32. {bt_cli-0.4.45 → bt_cli-0.4.47}/.github/workflows/ci.yml +0 -0
  33. {bt_cli-0.4.45 → bt_cli-0.4.47}/.github/workflows/release.yml +0 -0
  34. {bt_cli-0.4.45 → bt_cli-0.4.47}/.gitignore +0 -0
  35. {bt_cli-0.4.45 → bt_cli-0.4.47}/assets/cli-help.png +0 -0
  36. {bt_cli-0.4.45 → bt_cli-0.4.47}/assets/cli-output.png +0 -0
  37. {bt_cli-0.4.45 → bt_cli-0.4.47}/bt-cli.spec +0 -0
  38. {bt_cli-0.4.45 → bt_cli-0.4.47}/bt_entry.py +0 -0
  39. {bt_cli-0.4.45 → bt_cli-0.4.47}/epml-clients-server-side-filters-plan.md +0 -0
  40. {bt_cli-0.4.45 → bt_cli-0.4.47}/epml-implementation-plan.md +0 -0
  41. {bt_cli-0.4.45 → bt_cli-0.4.47}/pf-implementation-plan.md +0 -0
  42. {bt_cli-0.4.45 → bt_cli-0.4.47}/scripts/bt_entry.py +0 -0
  43. {bt_cli-0.4.45 → bt_cli-0.4.47}/scripts/pf_onboard.py +0 -0
  44. {bt_cli-0.4.45 → bt_cli-0.4.47}/scripts/sync-package-data.sh +0 -0
  45. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/commands/__init__.py +0 -0
  46. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/commands/configure.py +0 -0
  47. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/commands/learn.py +0 -0
  48. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/commands/quick.py +0 -0
  49. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/core/__init__.py +0 -0
  50. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/core/auth.py +0 -0
  51. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/core/client.py +0 -0
  52. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/core/csv_utils.py +0 -0
  53. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/core/errors.py +0 -0
  54. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/core/output.py +0 -0
  55. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/core/prompts.py +0 -0
  56. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/core/rest_debug.py +0 -0
  57. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/data/CLAUDE.md +0 -0
  58. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/data/__init__.py +0 -0
  59. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/data/skills/bt/SKILL.md +0 -0
  60. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/data/skills/entitle/SKILL.md +0 -0
  61. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/data/skills/epml/SKILL.md +0 -0
  62. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/data/skills/epmw/SKILL.md +0 -0
  63. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/data/skills/pra/SKILL.md +0 -0
  64. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/data/skills/pws/SKILL.md +0 -0
  65. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/entitle/__init__.py +0 -0
  66. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/entitle/client/__init__.py +0 -0
  67. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/entitle/client/base.py +0 -0
  68. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/entitle/commands/__init__.py +0 -0
  69. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/entitle/commands/accounts.py +0 -0
  70. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/entitle/commands/applications.py +0 -0
  71. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/entitle/commands/auth.py +0 -0
  72. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/entitle/commands/bundles.py +0 -0
  73. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/entitle/commands/integrations.py +0 -0
  74. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/entitle/commands/permissions.py +0 -0
  75. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/entitle/commands/policies.py +0 -0
  76. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/entitle/commands/requests.py +0 -0
  77. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/entitle/commands/resources.py +0 -0
  78. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/entitle/commands/roles.py +0 -0
  79. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/entitle/commands/users.py +0 -0
  80. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/entitle/commands/workflows.py +0 -0
  81. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/entitle/models/__init__.py +0 -0
  82. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/entitle/models/bundle.py +0 -0
  83. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/entitle/models/common.py +0 -0
  84. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/entitle/models/integration.py +0 -0
  85. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/entitle/models/permission.py +0 -0
  86. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/entitle/models/policy.py +0 -0
  87. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/entitle/models/resource.py +0 -0
  88. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/entitle/models/role.py +0 -0
  89. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/entitle/models/user.py +0 -0
  90. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/entitle/models/workflow.py +0 -0
  91. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/epml/__init__.py +0 -0
  92. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/epml/client/__init__.py +0 -0
  93. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/epml/client/base.py +0 -0
  94. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/epml/commands/__init__.py +0 -0
  95. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/epml/commands/audit.py +0 -0
  96. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/epml/commands/auth.py +0 -0
  97. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/epml/commands/client_pkg.py +0 -0
  98. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/epml/commands/clients.py +0 -0
  99. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/epml/commands/external_apis.py +0 -0
  100. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/epml/commands/hosts.py +0 -0
  101. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/epml/commands/iolog.py +0 -0
  102. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/epml/commands/license.py +0 -0
  103. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/epml/commands/quick.py +0 -0
  104. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/epml/commands/rbp_cmdgrps.py +0 -0
  105. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/epml/commands/rbp_entitlement.py +0 -0
  106. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/epml/commands/rbp_hostgrps.py +0 -0
  107. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/epml/commands/rbp_policy.py +0 -0
  108. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/epml/commands/rbp_roles.py +0 -0
  109. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/epml/commands/rbp_tests.py +0 -0
  110. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/epml/commands/rbp_tmdategrps.py +0 -0
  111. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/epml/commands/rbp_tx.py +0 -0
  112. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/epml/commands/rbp_usergrps.py +0 -0
  113. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/epml/commands/settings.py +0 -0
  114. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/epml/commands/siems.py +0 -0
  115. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/epml/commands/users.py +0 -0
  116. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/epml/models/__init__.py +0 -0
  117. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/epmw/__init__.py +0 -0
  118. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/epmw/client/__init__.py +0 -0
  119. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/epmw/client/base.py +0 -0
  120. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/epmw/commands/__init__.py +0 -0
  121. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/epmw/commands/audits.py +0 -0
  122. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/epmw/commands/auth.py +0 -0
  123. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/epmw/commands/computers.py +0 -0
  124. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/epmw/commands/events.py +0 -0
  125. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/epmw/commands/groups.py +0 -0
  126. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/epmw/commands/policies.py +0 -0
  127. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/epmw/commands/quick.py +0 -0
  128. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/epmw/commands/requests.py +0 -0
  129. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/epmw/commands/roles.py +0 -0
  130. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/epmw/commands/tasks.py +0 -0
  131. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/epmw/commands/users.py +0 -0
  132. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/epmw/models/__init__.py +0 -0
  133. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/pra/__init__.py +0 -0
  134. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/pra/client/__init__.py +0 -0
  135. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/pra/client/base.py +0 -0
  136. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/pra/commands/__init__.py +0 -0
  137. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/pra/commands/auth.py +0 -0
  138. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/pra/commands/group_policies.py +0 -0
  139. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/pra/commands/import_export.py +0 -0
  140. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/pra/commands/jump_clients.py +0 -0
  141. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/pra/commands/jump_groups.py +0 -0
  142. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/pra/commands/jump_items.py +0 -0
  143. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/pra/commands/jumpoints.py +0 -0
  144. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/pra/commands/policies.py +0 -0
  145. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/pra/commands/quick.py +0 -0
  146. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/pra/commands/teams.py +0 -0
  147. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/pra/commands/users.py +0 -0
  148. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/pra/commands/vault.py +0 -0
  149. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/pra/models/__init__.py +0 -0
  150. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/pra/models/common.py +0 -0
  151. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/pra/models/group_policy.py +0 -0
  152. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/pra/models/jump_client.py +0 -0
  153. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/pra/models/jump_group.py +0 -0
  154. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/pra/models/jump_item.py +0 -0
  155. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/pra/models/jumpoint.py +0 -0
  156. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/pra/models/team.py +0 -0
  157. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/pra/models/user.py +0 -0
  158. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/pra/models/vault.py +0 -0
  159. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/pws/__init__.py +0 -0
  160. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/pws/client/__init__.py +0 -0
  161. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/pws/client/base.py +0 -0
  162. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/pws/client/beyondinsight.py +0 -0
  163. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/pws/client/passwordsafe.py +0 -0
  164. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/pws/commands/__init__.py +0 -0
  165. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/pws/commands/accounts.py +0 -0
  166. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/pws/commands/assets.py +0 -0
  167. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/pws/commands/attributes.py +0 -0
  168. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/pws/commands/auth.py +0 -0
  169. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/pws/commands/clouds.py +0 -0
  170. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/pws/commands/config.py +0 -0
  171. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/pws/commands/credentials.py +0 -0
  172. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/pws/commands/databases.py +0 -0
  173. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/pws/commands/directories.py +0 -0
  174. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/pws/commands/functional.py +0 -0
  175. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/pws/commands/import_export.py +0 -0
  176. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/pws/commands/platforms.py +0 -0
  177. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/pws/commands/quick.py +0 -0
  178. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/pws/commands/search.py +0 -0
  179. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/pws/commands/secrets.py +0 -0
  180. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/pws/commands/systems.py +0 -0
  181. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/pws/commands/users.py +0 -0
  182. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/pws/commands/workgroups.py +0 -0
  183. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/pws/config.py +0 -0
  184. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/pws/models/__init__.py +0 -0
  185. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/pws/models/account.py +0 -0
  186. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/pws/models/asset.py +0 -0
  187. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/pws/models/common.py +0 -0
  188. {bt_cli-0.4.45 → bt_cli-0.4.47}/src/bt_cli/pws/models/system.py +0 -0
  189. {bt_cli-0.4.45 → bt_cli-0.4.47}/tests/__init__.py +0 -0
  190. {bt_cli-0.4.45 → bt_cli-0.4.47}/tests/conftest.py +0 -0
  191. {bt_cli-0.4.45 → bt_cli-0.4.47}/tests/core/__init__.py +0 -0
  192. {bt_cli-0.4.45 → bt_cli-0.4.47}/tests/core/test_auth.py +0 -0
  193. {bt_cli-0.4.45 → bt_cli-0.4.47}/tests/core/test_config.py +0 -0
  194. {bt_cli-0.4.45 → bt_cli-0.4.47}/tests/core/test_errors.py +0 -0
  195. {bt_cli-0.4.45 → bt_cli-0.4.47}/tests/core/test_rest_debug.py +0 -0
  196. {bt_cli-0.4.45 → bt_cli-0.4.47}/tests/entitle/__init__.py +0 -0
  197. {bt_cli-0.4.45 → bt_cli-0.4.47}/tests/entitle/test_client.py +0 -0
  198. {bt_cli-0.4.45 → bt_cli-0.4.47}/tests/entitle/test_commands.py +0 -0
  199. {bt_cli-0.4.45 → bt_cli-0.4.47}/tests/entitle-smoke-test.sh +0 -0
  200. {bt_cli-0.4.45 → bt_cli-0.4.47}/tests/epml/__init__.py +0 -0
  201. {bt_cli-0.4.45 → bt_cli-0.4.47}/tests/epml/test_client.py +0 -0
  202. {bt_cli-0.4.45 → bt_cli-0.4.47}/tests/epml/test_commands.py +0 -0
  203. {bt_cli-0.4.45 → bt_cli-0.4.47}/tests/epmw/__init__.py +0 -0
  204. {bt_cli-0.4.45 → bt_cli-0.4.47}/tests/epmw/test_client.py +0 -0
  205. {bt_cli-0.4.45 → bt_cli-0.4.47}/tests/epmw/test_commands.py +0 -0
  206. {bt_cli-0.4.45 → bt_cli-0.4.47}/tests/epmw-quick-test-plan.md +0 -0
  207. {bt_cli-0.4.45 → bt_cli-0.4.47}/tests/fixtures/__init__.py +0 -0
  208. {bt_cli-0.4.45 → bt_cli-0.4.47}/tests/fixtures/responses.py +0 -0
  209. {bt_cli-0.4.45 → bt_cli-0.4.47}/tests/integration/__init__.py +0 -0
  210. {bt_cli-0.4.45 → bt_cli-0.4.47}/tests/integration/conftest.py +0 -0
  211. {bt_cli-0.4.45 → bt_cli-0.4.47}/tests/integration/helpers.py +0 -0
  212. {bt_cli-0.4.45 → bt_cli-0.4.47}/tests/integration/test_entitle_integration.py +0 -0
  213. {bt_cli-0.4.45 → bt_cli-0.4.47}/tests/integration/test_epmw_integration.py +0 -0
  214. {bt_cli-0.4.45 → bt_cli-0.4.47}/tests/integration/test_epmw_lifecycle.py +0 -0
  215. {bt_cli-0.4.45 → bt_cli-0.4.47}/tests/integration/test_pra_integration.py +0 -0
  216. {bt_cli-0.4.45 → bt_cli-0.4.47}/tests/integration/test_pra_lifecycle.py +0 -0
  217. {bt_cli-0.4.45 → bt_cli-0.4.47}/tests/integration/test_pws_integration.py +0 -0
  218. {bt_cli-0.4.45 → bt_cli-0.4.47}/tests/integration/test_pws_lifecycle.py +0 -0
  219. {bt_cli-0.4.45 → bt_cli-0.4.47}/tests/pra/__init__.py +0 -0
  220. {bt_cli-0.4.45 → bt_cli-0.4.47}/tests/pra/test_client.py +0 -0
  221. {bt_cli-0.4.45 → bt_cli-0.4.47}/tests/pra/test_commands.py +0 -0
  222. {bt_cli-0.4.45 → bt_cli-0.4.47}/tests/pra-smoke-test.sh +0 -0
  223. {bt_cli-0.4.45 → bt_cli-0.4.47}/tests/pra-test-plan.md +0 -0
  224. {bt_cli-0.4.45 → bt_cli-0.4.47}/tests/pws/__init__.py +0 -0
  225. {bt_cli-0.4.45 → bt_cli-0.4.47}/tests/pws/test_client.py +0 -0
  226. {bt_cli-0.4.45 → bt_cli-0.4.47}/tests/pws/test_commands.py +0 -0
  227. {bt_cli-0.4.45 → bt_cli-0.4.47}/tests/pws-quick-test-plan.md +0 -0
  228. {bt_cli-0.4.45 → bt_cli-0.4.47}/tests/pws-smoke-test.sh +0 -0
@@ -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.45**
3
+ BeyondTrust Platform CLI for Password Safe, Entitle, PRA, EPM Windows, EPM Linux, and the BeyondTrust Secrets API. **Version: 0.4.47**
4
4
 
5
5
  ## Setup
6
6
 
@@ -31,6 +31,7 @@ Use these slash commands for detailed product guidance:
31
31
  | `/entitle` | Entitle - JIT access, bundles, workflows |
32
32
  | `/epmw` | EPM Windows - computers, policies, requests |
33
33
  | `/epml` | EPM Linux - RBP roles/cmdgrps/usergrps, policy, test suites, transactions |
34
+ | `/secrets` | Secrets API - folders, static, dynamic AWS credentials, leases, integrations |
34
35
 
35
36
  ## Command Structure
36
37
 
@@ -42,6 +43,7 @@ Use these slash commands for detailed product guidance:
42
43
  | Entitle | `bt entitle` | `integrations`, `resources`, `bundles`, `permissions` |
43
44
  | EPM Windows | `bt epmw` | `computers`, `groups`, `policies`, `requests`, `quick` |
44
45
  | EPM Linux | `bt epml` | `rbp` (cmdgrps/hostgrps/usergrps/tmdategrps/roles/policy/tests/tx), `settings`, `users`, `audit`, `siems`, `quick` |
46
+ | Secrets API | `bt secrets` | `folders`, `static`, `dynamic` (incl. `generate`), `leases`, `integrations` |
45
47
 
46
48
  ## Common Patterns
47
49
 
@@ -70,6 +72,7 @@ PASSWORD=$(bt pws quick checkout -s server -a admin --raw)
70
72
  | PRA | `per_page`/`current_page` (1-indexed) | Array (pagination in headers) |
71
73
  | EPMW | `pageNumber`/`pageSize` | `{"data": [...], "totalCount": N}` |
72
74
  | EPML | varies — `take`/`skip` (audit, siems), `start`/`len` (iologs), plain arrays | mostly arrays; some `{total, data}` |
75
+ | Secrets | `path`/`recursive`/`folder` query params | `{"data": [...]}` for lists; bare objects for single GETs |
73
76
 
74
77
  **Important:**
75
78
  - EPMW computers: Use `archive` not `delete` (405 error)
@@ -84,6 +87,8 @@ PASSWORD=$(bt pws quick checkout -s server -a admin --raw)
84
87
  - **EPML body-shape gotchas**: role create needs `action ∈ {A,R}`; hostgrp/usergrp create needs `type ∈ {I,E}` (Internal=static / External=directory-resolved); role assignments need `--kind S|R|B` for hostgrps/usergrps (S=Submit user, R=Run-as user); collection deletes use `?id=N` query params (CLI loops); child collections (commands/hosts/users/tmdates) have **no per-item delete** — `clear` nukes all, use `replace` to keep some.
85
88
  - **EPML role update is upsert-on-id (overwrite, not partial)**: hitting `POST /roles` with `{id, ...}` clears any field you don't include. The CLI's `roles update` does read-modify-write so it feels partial; child relations are filtered out of the merge so assignments survive. Direct `curl` users must send the full record.
86
89
  - **EPML role banner format**: `bt epml rbp roles create --banner-text "Title"` builds a `############`-framed message using `%rbprole%`/`%event%` server substitutions, mirroring the appliance's existing roles. See the worked role example in the EPM-L SKILL.md.
90
+ - **Secrets dynamic path is `/dynamic`** (NOT `/dynamic-secrets` as the doc claims); version header is `2026-02-16` (NOT 2026-01-02); `dynamic/{name}/generate` returns 201; `/leases/revoke` is scope-gated and 403 is the expected lab response — `bt secrets leases revoke` exits 3 with a clean warning, not generic failure. Documented in `src/bt_cli/data/skills/secrets/SKILL.md`.
91
+ - **Secrets `generate` mints real AWS STS credentials and is audited** — don't loop it in dev. Use `dynamic list`/`get` + `leases list` for browsing.
87
92
 
88
93
  ## Functional vs Managed Accounts
89
94
 
@@ -112,7 +117,8 @@ src/bt_cli/
112
117
  ├── pra/ # PRA
113
118
  ├── entitle/ # Entitle
114
119
  ├── epmw/ # EPM Windows
115
- └── epml/ # EPM Linux (PAT auth, /site/<id>/epm/linux/... URLs)
120
+ ├── epml/ # EPM Linux (PAT auth, /site/<id>/epm/linux/... URLs)
121
+ └── secrets/ # BeyondTrust Secrets API (PAT auth, /site/<id>/secrets/... URLs)
116
122
  ```
117
123
 
118
124
  Each product follows: `client/` (API), `commands/` (CLI), `models/` (Pydantic)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: bt-cli
3
- Version: 0.4.45
3
+ Version: 0.4.47
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
@@ -57,6 +57,7 @@ Unofficial BeyondTrust Platform CLI - manage privileged access across your envir
57
57
  | **Entitle** | `bt entitle` | Just-in-time access requests and approval workflows |
58
58
  | **PRA** | `bt pra` | Privileged remote access - jump items, sessions, vault |
59
59
  | **EPM Windows** | `bt epmw` | Endpoint privilege management - computers, policies, admin requests |
60
+ | **Secrets API** | `bt secrets` | BeyondTrust Secrets API — folders, static secrets, dynamic AWS credentials, leases |
60
61
 
61
62
  ## Installation
62
63
 
@@ -105,6 +106,33 @@ export BT_EPM_CLIENT_ID=your-client-id
105
106
  export BT_EPM_CLIENT_SECRET=your-client-secret
106
107
  ```
107
108
 
109
+ ### Secrets API
110
+
111
+ PAT-bearer auth against the BeyondTrust public API gateway. Mint a PAT at
112
+ [app.beyondtrust.io](https://app.beyondtrust.io).
113
+
114
+ ```bash
115
+ export BT_SECRETS_API_URL=https://api.beyondtrust.io # default
116
+ export BT_SECRETS_SITE_ID=<site-uuid> # required
117
+ export BT_SECRETS_PAT=PAT_xxx # required
118
+ export BT_SECRETS_API_VERSION=2026-02-16 # default (doc page is stale)
119
+ ```
120
+
121
+ Common commands:
122
+
123
+ ```bash
124
+ bt secrets folders list -p lab # filter server-side (recursive default)
125
+ bt secrets dynamic list -p lab # all dynamic-secret configs under lab/
126
+ bt secrets dynamic generate lab/aws/ec2-readonly # mint shell `export AWS_*` lines
127
+ eval $(bt secrets dynamic generate lab/aws/ec2-readonly) # load into current shell
128
+ bt secrets leases list lab/aws/ec2-readonly # who has active leases
129
+ bt secrets static get lab/db/creds # read a static secret (default JSON)
130
+ ```
131
+
132
+ `bt secrets dynamic generate` mints **real** AWS STS credentials and is
133
+ audited — don't loop it. `bt secrets leases revoke` is scope-gated; most
134
+ PATs return 403 and exit code 3 with a clean warning. TTL still revokes.
135
+
108
136
  ### Using a .env File
109
137
 
110
138
  Create a `.env` file and source it before running commands:
@@ -121,6 +149,7 @@ bt pws auth test # Test Password Safe connection
121
149
  bt entitle auth test # Test Entitle connection
122
150
  bt pra auth test # Test PRA connection
123
151
  bt epmw auth test # Test EPM Windows connection
152
+ bt secrets auth test # Test Secrets API connection
124
153
  ```
125
154
 
126
155
  ---
@@ -10,6 +10,7 @@ Unofficial BeyondTrust Platform CLI - manage privileged access across your envir
10
10
  | **Entitle** | `bt entitle` | Just-in-time access requests and approval workflows |
11
11
  | **PRA** | `bt pra` | Privileged remote access - jump items, sessions, vault |
12
12
  | **EPM Windows** | `bt epmw` | Endpoint privilege management - computers, policies, admin requests |
13
+ | **Secrets API** | `bt secrets` | BeyondTrust Secrets API — folders, static secrets, dynamic AWS credentials, leases |
13
14
 
14
15
  ## Installation
15
16
 
@@ -58,6 +59,33 @@ export BT_EPM_CLIENT_ID=your-client-id
58
59
  export BT_EPM_CLIENT_SECRET=your-client-secret
59
60
  ```
60
61
 
62
+ ### Secrets API
63
+
64
+ PAT-bearer auth against the BeyondTrust public API gateway. Mint a PAT at
65
+ [app.beyondtrust.io](https://app.beyondtrust.io).
66
+
67
+ ```bash
68
+ export BT_SECRETS_API_URL=https://api.beyondtrust.io # default
69
+ export BT_SECRETS_SITE_ID=<site-uuid> # required
70
+ export BT_SECRETS_PAT=PAT_xxx # required
71
+ export BT_SECRETS_API_VERSION=2026-02-16 # default (doc page is stale)
72
+ ```
73
+
74
+ Common commands:
75
+
76
+ ```bash
77
+ bt secrets folders list -p lab # filter server-side (recursive default)
78
+ bt secrets dynamic list -p lab # all dynamic-secret configs under lab/
79
+ bt secrets dynamic generate lab/aws/ec2-readonly # mint shell `export AWS_*` lines
80
+ eval $(bt secrets dynamic generate lab/aws/ec2-readonly) # load into current shell
81
+ bt secrets leases list lab/aws/ec2-readonly # who has active leases
82
+ bt secrets static get lab/db/creds # read a static secret (default JSON)
83
+ ```
84
+
85
+ `bt secrets dynamic generate` mints **real** AWS STS credentials and is
86
+ audited — don't loop it. `bt secrets leases revoke` is scope-gated; most
87
+ PATs return 403 and exit code 3 with a clean warning. TTL still revokes.
88
+
61
89
  ### Using a .env File
62
90
 
63
91
  Create a `.env` file and source it before running commands:
@@ -74,6 +102,7 @@ bt pws auth test # Test Password Safe connection
74
102
  bt entitle auth test # Test Entitle connection
75
103
  bt pra auth test # Test PRA connection
76
104
  bt epmw auth test # Test EPM Windows connection
105
+ bt secrets auth test # Test Secrets API connection
77
106
  ```
78
107
 
79
108
  ---
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "bt-cli"
7
- version = "0.4.45"
7
+ version = "0.4.47"
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.45"
3
+ __version__ = "0.4.47"
@@ -89,6 +89,12 @@ def _get_epml_app() -> typer.Typer:
89
89
  return epml_app
90
90
 
91
91
 
92
+ def _get_secrets_app() -> typer.Typer:
93
+ """Lazy load BeyondTrust Secrets API commands."""
94
+ from .secrets.commands import app as secrets_app
95
+ return secrets_app
96
+
97
+
92
98
  def _get_configure_app() -> typer.Typer:
93
99
  """Lazy load configure commands."""
94
100
  from .commands.configure import app as configure_app
@@ -134,6 +140,11 @@ try:
134
140
  except Exception:
135
141
  pass # EPML module not ready yet
136
142
 
143
+ try:
144
+ app.add_typer(_get_secrets_app(), name="secrets", help="BeyondTrust Secrets API commands")
145
+ except Exception:
146
+ pass # Secrets module not ready yet
147
+
137
148
  try:
138
149
  app.add_typer(_get_configure_app(), name="configure", help="Configure bt-cli settings")
139
150
  except Exception:
@@ -288,9 +299,9 @@ def tree_command(
288
299
 
289
300
  if product:
290
301
  product = product.lower()
291
- if product not in ["pws", "pra", "entitle", "epmw", "epml", "quick", "configure"]:
302
+ if product not in ["pws", "pra", "entitle", "epmw", "epml", "secrets", "quick", "configure"]:
292
303
  console.print(f"[red]Unknown product: {product}[/red]")
293
- console.print("Available: pws, pra, entitle, epmw, epml, quick, configure")
304
+ console.print("Available: pws, pra, entitle, epmw, epml, secrets, quick, configure")
294
305
  raise typer.Exit(1)
295
306
 
296
307
  tree = Tree("[bold cyan]bt[/bold cyan]")
@@ -404,6 +415,16 @@ def tree_command(
404
415
  rbp.add("tx status|begin|commit|rollback|abort")
405
416
  epml.add("[green]quick[/green] tests-then-deploy")
406
417
 
418
+ # Secrets API
419
+ if not product or product == "secrets":
420
+ secrets = tree.add("[bold yellow]secrets[/bold yellow] - BeyondTrust Secrets API")
421
+ secrets.add("[green]auth[/green] test|status")
422
+ secrets.add("[green]folders[/green] list|get|create|delete")
423
+ secrets.add("[green]static[/green] list|get|create|update|delete")
424
+ secrets.add("[green]dynamic[/green] list|get|generate|create|delete")
425
+ secrets.add("[green]leases[/green] list|get|revoke")
426
+ secrets.add("[green]integrations[/green] list|get|create|update|delete")
427
+
407
428
  # Quick
408
429
  if not product or product == "quick":
409
430
  quick = tree.add("[bold yellow]quick[/bold yellow] - Cross-product workflows")
@@ -591,6 +612,31 @@ def _get_all_commands() -> list[tuple[str, str]]:
591
612
  ("bt epml rbp tests tests delete", "Delete a test"),
592
613
  ("bt epml rbp tests run", "Run a test suite, exit non-zero on failure"),
593
614
  ("bt epml quick tests-then-deploy", "Run tests, commit or rollback the open RBP transaction"),
615
+ # Secrets API
616
+ ("bt secrets auth test", "Test BeyondTrust Secrets API connection"),
617
+ ("bt secrets auth status", "Show Secrets API configuration status"),
618
+ ("bt secrets folders list", "List folders (server-side filter)"),
619
+ ("bt secrets folders get", "Get folder metadata"),
620
+ ("bt secrets folders create", "Create a folder"),
621
+ ("bt secrets folders delete", "Delete a folder"),
622
+ ("bt secrets static list", "List static secrets"),
623
+ ("bt secrets static get", "Get a static secret value"),
624
+ ("bt secrets static create", "Create a static secret"),
625
+ ("bt secrets static update", "Update a static secret (bumps version)"),
626
+ ("bt secrets static delete", "Delete a static secret"),
627
+ ("bt secrets dynamic list", "List dynamic-secret configs"),
628
+ ("bt secrets dynamic get", "Get a dynamic-secret config"),
629
+ ("bt secrets dynamic generate", "Mint a fresh dynamic credential (e.g. AWS STS)"),
630
+ ("bt secrets dynamic create", "Create a dynamic-secret config"),
631
+ ("bt secrets dynamic delete", "Delete a dynamic-secret config"),
632
+ ("bt secrets leases list", "List active leases for a dynamic secret"),
633
+ ("bt secrets leases get", "Get lease details by id"),
634
+ ("bt secrets leases revoke", "Revoke a lease (scope-gated; lab returns 403)"),
635
+ ("bt secrets integrations list", "List integrations"),
636
+ ("bt secrets integrations get", "Get an integration"),
637
+ ("bt secrets integrations create", "Create an integration"),
638
+ ("bt secrets integrations update", "Update an integration (PUT or --partial PATCH)"),
639
+ ("bt secrets integrations delete", "Delete an integration"),
594
640
  # Quick
595
641
  ("bt quick pasm-onboard", "Onboard host to PWS + PRA (Total PASM)"),
596
642
  ("bt quick pasm-offboard", "Offboard host from PWS + PRA"),
@@ -817,6 +863,11 @@ def whoami(
817
863
  if epml_result:
818
864
  results.append(epml_result)
819
865
 
866
+ # Test Secrets API
867
+ secrets_result = _test_secrets_connection()
868
+ if secrets_result:
869
+ results.append(secrets_result)
870
+
820
871
  if not results:
821
872
  console.print("[yellow]No products configured.[/yellow]")
822
873
  console.print("\nTo configure products, set environment variables or run:")
@@ -1024,6 +1075,39 @@ def _test_epml_connection() -> Optional[dict]:
1024
1075
  }
1025
1076
 
1026
1077
 
1078
+ def _test_secrets_connection() -> Optional[dict]:
1079
+ """Test BeyondTrust Secrets API connection and return status."""
1080
+ try:
1081
+ from .core.config import load_secrets_config
1082
+ from .secrets.client import get_client
1083
+
1084
+ config = load_secrets_config()
1085
+ masked_pat = config.pat[:12] + "..." if len(config.pat) > 12 else "***"
1086
+ result = {
1087
+ "product": "Secrets API",
1088
+ "url": f"{config.api_url}/site/{config.site_id}/secrets",
1089
+ "auth_method": f"PAT ({masked_pat})",
1090
+ "connected": False,
1091
+ }
1092
+
1093
+ with get_client() as client:
1094
+ folders = client.list_folders()
1095
+ result["connected"] = True
1096
+ result["user_info"] = f"site {config.site_id[:8]}…, {len(folders)} root folder(s)"
1097
+
1098
+ return result
1099
+ except ValueError:
1100
+ return None
1101
+ except Exception as e:
1102
+ return {
1103
+ "product": "Secrets API",
1104
+ "url": "",
1105
+ "auth_method": "-",
1106
+ "connected": False,
1107
+ "error": str(e)[:50],
1108
+ }
1109
+
1110
+
1027
1111
  def run() -> None:
1028
1112
  """Run the CLI application."""
1029
1113
  app()
@@ -116,6 +116,35 @@ class EPMWConfig(ProductConfig):
116
116
  )
117
117
 
118
118
 
119
+ @dataclass
120
+ class SecretsConfig(ProductConfig):
121
+ """BeyondTrust Secrets API configuration.
122
+
123
+ Uses Personal Access Token (PAT) bearer authentication against the
124
+ BeyondTrust public API gateway. URLs are composed as:
125
+
126
+ {api_url}/site/{site_id}/secrets/<path>
127
+
128
+ The `api_version` is sent as the `bt-secrets-api-version` header on
129
+ every request. The live API version differs from the public doc page
130
+ (which lists 2026-01-02 — that gets rejected); the default below is
131
+ what the server currently accepts.
132
+ """
133
+
134
+ site_id: str = ""
135
+ pat: str = ""
136
+ api_version: str = "2026-02-16"
137
+
138
+ def validate(self) -> None:
139
+ """Validate configuration."""
140
+ if not self.api_url:
141
+ raise ValueError("BT_SECRETS_API_URL is required")
142
+ if not self.site_id:
143
+ raise ValueError("BT_SECRETS_SITE_ID is required")
144
+ if not self.pat:
145
+ raise ValueError("BT_SECRETS_PAT is required")
146
+
147
+
119
148
  @dataclass
120
149
  class EPMLConfig(ProductConfig):
121
150
  """EPM Linux configuration.
@@ -448,11 +477,49 @@ def load_epml_config(env_file: Optional[str] = None, profile: Optional[str] = No
448
477
  return config
449
478
 
450
479
 
480
+ def load_secrets_config(env_file: Optional[str] = None, profile: Optional[str] = None) -> SecretsConfig:
481
+ """Load BeyondTrust Secrets API configuration.
482
+
483
+ Configuration sources (in order of precedence):
484
+ 1. Environment variables
485
+ 2. Config file (~/.bt-cli/config.yaml)
486
+
487
+ Environment variables:
488
+ BT_SECRETS_API_URL - Gateway URL (default: https://api.beyondtrust.io)
489
+ BT_SECRETS_SITE_ID - Site UUID (required)
490
+ BT_SECRETS_PAT - Personal Access Token (required)
491
+ BT_SECRETS_API_VERSION - API version header (default: 2026-02-16)
492
+ BT_SECRETS_VERIFY_SSL - SSL verification (default: true)
493
+ BT_SECRETS_TIMEOUT - Request timeout in seconds (default: 30)
494
+ """
495
+ if env_file:
496
+ load_dotenv(env_file)
497
+ else:
498
+ load_dotenv()
499
+
500
+ profile = profile or _get_profile()
501
+ layered = get_layered_config("secrets", profile)
502
+
503
+ if "pat" in layered:
504
+ layered["pat"] = _resolve_value(layered["pat"])
505
+
506
+ config = SecretsConfig(
507
+ api_url=layered.get("api_url") or os.getenv("BT_SECRETS_API_URL", "https://api.beyondtrust.io"),
508
+ site_id=layered.get("site_id") or os.getenv("BT_SECRETS_SITE_ID", ""),
509
+ pat=layered.get("pat") or os.getenv("BT_SECRETS_PAT", ""),
510
+ api_version=layered.get("api_version") or os.getenv("BT_SECRETS_API_VERSION", "2026-02-16"),
511
+ verify_ssl=_to_bool(layered.get("verify_ssl")) if "verify_ssl" in layered else _get_bool(os.getenv("BT_SECRETS_VERIFY_SSL")),
512
+ timeout=_to_float(layered.get("timeout")) if "timeout" in layered else _get_float(os.getenv("BT_SECRETS_TIMEOUT"), 30.0),
513
+ )
514
+ config.validate()
515
+ return config
516
+
517
+
451
518
  def load_config(product: str, env_file: Optional[str] = None) -> ProductConfig:
452
519
  """Load configuration for a specific product.
453
520
 
454
521
  Args:
455
- product: Product name ('pws', 'entitle', 'pra', 'epmw')
522
+ product: Product name ('pws', 'entitle', 'pra', 'epmw', 'epml', 'secrets')
456
523
  env_file: Optional path to .env file
457
524
 
458
525
  Returns:
@@ -467,6 +534,7 @@ def load_config(product: str, env_file: Optional[str] = None) -> ProductConfig:
467
534
  "pra": load_pra_config,
468
535
  "epmw": load_epmw_config,
469
536
  "epml": load_epml_config,
537
+ "secrets": load_secrets_config,
470
538
  }
471
539
 
472
540
  loader = loaders.get(product.lower())
@@ -95,6 +95,33 @@ PRODUCTS = {
95
95
  "timeout": {"prompt": "Timeout (seconds)", "required": False, "secret": False, "default": 30},
96
96
  },
97
97
  },
98
+ "secrets": {
99
+ "name": "BeyondTrust Secrets API",
100
+ "fields": {
101
+ "api_url": {
102
+ "prompt": "Gateway URL",
103
+ "required": True,
104
+ "secret": False,
105
+ "default": "https://api.beyondtrust.io",
106
+ "example": "https://api.beyondtrust.io",
107
+ },
108
+ "site_id": {
109
+ "prompt": "Site ID (UUID)",
110
+ "required": True,
111
+ "secret": False,
112
+ "example": "7f735e1b-5ecb-49fa-87e8-eddf54a9c745",
113
+ },
114
+ "pat": {"prompt": "Personal Access Token", "required": True, "secret": True},
115
+ "api_version": {
116
+ "prompt": "API version (bt-secrets-api-version header)",
117
+ "required": False,
118
+ "secret": False,
119
+ "default": "2026-02-16",
120
+ },
121
+ "verify_ssl": {"prompt": "Verify SSL", "required": False, "secret": False, "default": True},
122
+ "timeout": {"prompt": "Timeout (seconds)", "required": False, "secret": False, "default": 30},
123
+ },
124
+ },
98
125
  "epml": {
99
126
  "name": "EPM Linux",
100
127
  "fields": {
@@ -328,6 +355,7 @@ def _get_env_prefix(product: str) -> str:
328
355
  "pra": "BT_PRA",
329
356
  "epmw": "BT_EPM",
330
357
  "epml": "BT_EPML",
358
+ "secrets": "BT_SECRETS",
331
359
  }
332
360
  return prefixes.get(product, f"BT_{product.upper()}")
333
361
 
@@ -359,6 +387,13 @@ def _get_env_mappings(product: str) -> dict[str, str]:
359
387
  # EPM-L doesn't use api_key/client_id/client_secret — drop the inherited mappings
360
388
  for unused in ("api_key", "client_id", "client_secret"):
361
389
  mappings.pop(unused, None)
390
+ elif product == "secrets":
391
+ mappings["site_id"] = f"{prefix}_SITE_ID"
392
+ mappings["pat"] = f"{prefix}_PAT"
393
+ mappings["api_version"] = f"{prefix}_API_VERSION"
394
+ # Secrets API doesn't use api_key/client_id/client_secret — drop the inherited mappings
395
+ for unused in ("api_key", "client_id", "client_secret"):
396
+ mappings.pop(unused, None)
362
397
 
363
398
  return mappings
364
399
 
@@ -0,0 +1,212 @@
1
+ ---
2
+ name: secrets
3
+ description: BeyondTrust Secrets API commands — folders, static secrets, dynamic AWS credentials, leases, integrations. Use when minting STS credentials, managing secret folders/values, or browsing/revoking dynamic-credential leases.
4
+ ---
5
+
6
+ # Secrets API Commands (`bt secrets`)
7
+
8
+ Direct CLI for the BeyondTrust Secrets API. PAT-bearer auth, same gateway as
9
+ EPM-L (`api.beyondtrust.io`). **Not** a wrapper around Entitle.
10
+
11
+ ## IMPORTANT: Destructive Operations
12
+
13
+ **ALWAYS confirm with the user before:**
14
+ - `bt secrets folders delete --recursive` — wipes the folder and all contents
15
+ - `bt secrets folders delete --permanent` — bypasses soft-delete
16
+ - `bt secrets static delete` / `dynamic delete` — removes the secret/config
17
+ - `bt secrets integrations delete` — disables downstream dynamic generation
18
+
19
+ List affected resources first, then ask for explicit confirmation. Every
20
+ delete supports `--force` to skip the prompt — only use it when the user has
21
+ already confirmed.
22
+
23
+ ## IMPORTANT: Generate Sparingly
24
+
25
+ `bt secrets dynamic generate <path>` mints **real** AWS STS credentials and
26
+ shows up in the audit trail. Don't loop it during development. For exploring,
27
+ use `dynamic list` / `dynamic get` / `leases list` — those don't mint.
28
+
29
+ ## Configuration
30
+
31
+ Set these before running commands. PAT comes from `app.beyondtrust.io`.
32
+
33
+ ```bash
34
+ export BT_SECRETS_API_URL=https://api.beyondtrust.io # default
35
+ export BT_SECRETS_SITE_ID=<site-uuid> # required
36
+ export BT_SECRETS_PAT=PAT_xxx # required
37
+ export BT_SECRETS_API_VERSION=2026-02-16 # default; doc page is stale
38
+ ```
39
+
40
+ ```bash
41
+ bt secrets auth test # verifies PAT + site + version-header reach the API
42
+ bt secrets auth status # env-var view (no network)
43
+ ```
44
+
45
+ ## Path Semantics
46
+
47
+ Every item lives at a "path" like `lab/aws/cloudwatch-readonly`. The CLI
48
+ auto-splits the trailing segment as the `name` and the rest as the parent
49
+ `folder`, so users (and you) pass single paths everywhere. The folder may be
50
+ empty for top-level items.
51
+
52
+ ```bash
53
+ bt secrets dynamic get lab/aws/cloudwatch-readonly # folder=lab/aws, name=cloudwatch-readonly
54
+ bt secrets folders get lab/aws # folder=lab, name=aws
55
+ ```
56
+
57
+ ## Folders
58
+
59
+ `list` is **recursive by default** (mirrors how users browse the UI tree).
60
+ Pass `--shallow` / `-S` for direct children only.
61
+
62
+ ```bash
63
+ bt secrets folders list # all folders, anywhere in the site
64
+ bt secrets folders list -p lab # everything under lab/ recursively
65
+ bt secrets folders list -p lab --shallow # only direct children of lab/
66
+ bt secrets folders get lab/aws # metadata
67
+ bt secrets folders create lab/aws/staging
68
+ bt secrets folders delete lab/aws/staging
69
+ bt secrets folders delete lab/aws/staging --recursive --force # nuke contents
70
+ ```
71
+
72
+ ## Static Secrets
73
+
74
+ Versioned key/value secrets. The body is a JSON object — pass it inline via
75
+ `--data` or from disk via `--data-file`.
76
+
77
+ ```bash
78
+ bt secrets static list # everything in the site (recursive default)
79
+ bt secrets static list -p lab # everything under lab/
80
+ bt secrets static list -p lab --shallow # direct children of lab/ only
81
+ bt secrets static get lab/db-pass # default JSON output (values are structured)
82
+ bt secrets static get lab/db-pass --version 2 # specific historical version
83
+ bt secrets static create lab/db-pass -d '{"password":"hunter2","username":"admin"}'
84
+ bt secrets static update lab/db-pass -d '{"password":"newpass"}' # bumps version, returns 204
85
+ bt secrets static delete lab/db-pass --force
86
+ ```
87
+
88
+ ## Dynamic Secrets
89
+
90
+ Configurations that mint short-lived credentials on demand (AWS STS today).
91
+
92
+ ```bash
93
+ bt secrets dynamic list # all dynamic-secret configs in the site
94
+ bt secrets dynamic list -p lab # everything under lab/
95
+ bt secrets dynamic list -p lab --shallow # only items directly at lab/ (usually none — they nest)
96
+ bt secrets dynamic get lab/aws/ec2-readonly
97
+
98
+ # Mint fresh credentials — default prints shell `export AWS_*` lines.
99
+ bt secrets dynamic generate lab/aws/ec2-readonly
100
+ eval $(bt secrets dynamic generate lab/aws/ec2-readonly) # one-liner load into shell
101
+ aws sts get-caller-identity # verify
102
+
103
+ # Other formats:
104
+ bt secrets dynamic generate lab/aws/ec2-readonly --format json # full payload w/ leaseId
105
+ bt secrets dynamic generate lab/aws/ec2-readonly --format env # KEY=value (no `export`)
106
+ bt secrets dynamic generate lab/aws/ec2-readonly --format dotenv # KEY="value" for .env files
107
+ ```
108
+
109
+ Create dynamic-secret config (AWS example):
110
+
111
+ ```bash
112
+ bt secrets dynamic create lab/aws/my-new-role -d '{
113
+ "type":"aws",
114
+ "integrationName":"AWS-Green-SE-Lab",
115
+ "credentialType":"assumed_role",
116
+ "roleArn":"arn:aws:iam::614212341445:role/beyondtrust/my-role",
117
+ "ttl":900
118
+ }'
119
+ ```
120
+
121
+ ## Leases
122
+
123
+ Each `generate` call creates a lease. Leases auto-expire at TTL; the
124
+ `revoke` endpoint is **scope-gated** and most lab PATs return 403 — that's
125
+ not a generic error, it's the expected "you can't revoke" signal.
126
+
127
+ ```bash
128
+ bt secrets leases list lab/aws/ec2-readonly
129
+ bt secrets leases get <lease-uuid> # rich detail: externalEntityId (assumed-role ARN), etc.
130
+ bt secrets leases revoke <lease-uuid> # exit 3 + warning if PAT lacks the revoke scope
131
+ ```
132
+
133
+ ## Integrations
134
+
135
+ External-system connections (currently AWS). Required before a dynamic-secret
136
+ config can reference an `integrationName`.
137
+
138
+ ```bash
139
+ bt secrets integrations list
140
+ bt secrets integrations list -t aws
141
+ bt secrets integrations get my-aws
142
+ bt secrets integrations create my-aws -d '{
143
+ "type":"aws",
144
+ "roleArn":"arn:aws:iam::123456789012:role/MyRole",
145
+ "externalId":"ext-id"
146
+ }'
147
+ bt secrets integrations update my-aws -d '{...}' # PUT (full replace)
148
+ bt secrets integrations update my-aws --partial -d '{...}' # PATCH (partial)
149
+ bt secrets integrations delete my-aws --force
150
+ ```
151
+
152
+ ## API Quirks (Gotchas)
153
+
154
+ - **Dynamic path is `/dynamic`, NOT `/dynamic-secrets`** — the static doc
155
+ page hasn't caught up to the live API. The client handles this; never
156
+ hand-hack URLs.
157
+ - **API version**: header value is `bt-secrets-api-version: 2026-02-16`. The
158
+ doc says `2026-01-02` and the server rejects it.
159
+ - **POST `/dynamic/{name}/generate` returns 201**, not 200. The client
160
+ accepts both via `raise_for_status` semantics.
161
+ - **`/leases/revoke` is scope-gated** — `bt secrets leases revoke` exits 3
162
+ with a clean warning when the server returns 403. TTL still expires the
163
+ lease eventually.
164
+ - **Rate limit**: 500/window. The client retries 429 once, honoring
165
+ `x-ratelimit-reset` (clamped to ≤ 30s so a bogus header can't park the
166
+ CLI).
167
+ - **PAT redaction**: errors and logs route through `bt_cli.core.errors` which
168
+ scrubs `Authorization` headers and bearer tokens before display. Never
169
+ add a `print(client.pat)` to debug — the PAT must never echo back.
170
+
171
+ ## Output Formats
172
+
173
+ - All `list` commands default to rich tables; pass `-o json` for scripting.
174
+ - `get` commands default to JSON since values are structured.
175
+ - `dynamic generate` defaults to `export` shell lines — designed for
176
+ `eval $(...)`.
177
+
178
+ ## Common Workflows
179
+
180
+ ### Mint creds, run an AWS command, let TTL revoke
181
+
182
+ ```bash
183
+ eval $(bt secrets dynamic generate lab/aws/ec2-readonly)
184
+ aws ec2 describe-instances --max-results 5
185
+ # (don't bother revoking — TTL handles it)
186
+ ```
187
+
188
+ ### Audit who currently has access via a dynamic config
189
+
190
+ ```bash
191
+ bt secrets leases list lab/aws/full-admin -o json \
192
+ | jq -r '.[] | "\(.leaseId) \(.expiration) \(.externalEntityId)"'
193
+ ```
194
+
195
+ ### Browse the tree
196
+
197
+ ```bash
198
+ bt secrets folders list -p lab
199
+ bt secrets static list -p lab
200
+ bt secrets dynamic list -p lab
201
+ ```
202
+
203
+ `list` is recursive by default; the API otherwise only returns items
204
+ directly at the requested path (this trips up newcomers — passing
205
+ `-p lab` returns 0 in shallow mode because the items live in `lab/aws`).
206
+
207
+ ## API Notes
208
+
209
+ - Base URL: `https://api.beyondtrust.io/site/<site-id>/secrets`
210
+ - Auth: `Authorization: Bearer <PAT>`
211
+ - Version header: `bt-secrets-api-version: 2026-02-16`
212
+ - Rate limit: 500/window — respect `x-ratelimit-reset` on 429.
@@ -0,0 +1 @@
1
+ """BeyondTrust Secrets API module."""