bt-cli 0.4.44__tar.gz → 0.4.46__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 (227) hide show
  1. {bt_cli-0.4.44 → bt_cli-0.4.46}/CLAUDE.md +8 -2
  2. {bt_cli-0.4.44 → bt_cli-0.4.46}/PKG-INFO +30 -1
  3. {bt_cli-0.4.44 → bt_cli-0.4.46}/README.md +29 -0
  4. {bt_cli-0.4.44 → bt_cli-0.4.46}/pyproject.toml +1 -1
  5. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/__init__.py +1 -1
  6. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/cli.py +86 -2
  7. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/commands/configure.py +17 -2
  8. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/core/config.py +69 -1
  9. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/core/config_file.py +47 -1
  10. bt_cli-0.4.46/src/bt_cli/data/skills/secrets/SKILL.md +201 -0
  11. bt_cli-0.4.46/src/bt_cli/secrets/__init__.py +1 -0
  12. bt_cli-0.4.46/src/bt_cli/secrets/client/__init__.py +5 -0
  13. bt_cli-0.4.46/src/bt_cli/secrets/client/base.py +404 -0
  14. bt_cli-0.4.46/src/bt_cli/secrets/commands/__init__.py +23 -0
  15. bt_cli-0.4.46/src/bt_cli/secrets/commands/auth.py +67 -0
  16. bt_cli-0.4.46/src/bt_cli/secrets/commands/dynamic.py +295 -0
  17. bt_cli-0.4.46/src/bt_cli/secrets/commands/folders.py +153 -0
  18. bt_cli-0.4.46/src/bt_cli/secrets/commands/integrations.py +201 -0
  19. bt_cli-0.4.46/src/bt_cli/secrets/commands/leases.py +122 -0
  20. bt_cli-0.4.46/src/bt_cli/secrets/commands/static.py +251 -0
  21. bt_cli-0.4.46/src/bt_cli/secrets/models/__init__.py +1 -0
  22. bt_cli-0.4.46/tests/secrets/__init__.py +1 -0
  23. bt_cli-0.4.46/tests/secrets/test_client.py +263 -0
  24. bt_cli-0.4.46/tests/secrets/test_commands.py +291 -0
  25. {bt_cli-0.4.44 → bt_cli-0.4.46}/.claude/skills/bt/SKILL.md +0 -0
  26. {bt_cli-0.4.44 → bt_cli-0.4.46}/.claude/skills/entitle/SKILL.md +0 -0
  27. {bt_cli-0.4.44 → bt_cli-0.4.46}/.claude/skills/epml/SKILL.md +0 -0
  28. {bt_cli-0.4.44 → bt_cli-0.4.46}/.claude/skills/epmw/SKILL.md +0 -0
  29. {bt_cli-0.4.44 → bt_cli-0.4.46}/.claude/skills/pra/SKILL.md +0 -0
  30. {bt_cli-0.4.44 → bt_cli-0.4.46}/.claude/skills/pws/SKILL.md +0 -0
  31. {bt_cli-0.4.44 → bt_cli-0.4.46}/.env.example +0 -0
  32. {bt_cli-0.4.44 → bt_cli-0.4.46}/.github/workflows/ci.yml +0 -0
  33. {bt_cli-0.4.44 → bt_cli-0.4.46}/.github/workflows/release.yml +0 -0
  34. {bt_cli-0.4.44 → bt_cli-0.4.46}/.gitignore +0 -0
  35. {bt_cli-0.4.44 → bt_cli-0.4.46}/assets/cli-help.png +0 -0
  36. {bt_cli-0.4.44 → bt_cli-0.4.46}/assets/cli-output.png +0 -0
  37. {bt_cli-0.4.44 → bt_cli-0.4.46}/bt-cli.spec +0 -0
  38. {bt_cli-0.4.44 → bt_cli-0.4.46}/bt_entry.py +0 -0
  39. {bt_cli-0.4.44 → bt_cli-0.4.46}/epml-clients-server-side-filters-plan.md +0 -0
  40. {bt_cli-0.4.44 → bt_cli-0.4.46}/epml-implementation-plan.md +0 -0
  41. {bt_cli-0.4.44 → bt_cli-0.4.46}/pf-implementation-plan.md +0 -0
  42. {bt_cli-0.4.44 → bt_cli-0.4.46}/scripts/bt_entry.py +0 -0
  43. {bt_cli-0.4.44 → bt_cli-0.4.46}/scripts/pf_onboard.py +0 -0
  44. {bt_cli-0.4.44 → bt_cli-0.4.46}/scripts/sync-package-data.sh +0 -0
  45. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/commands/__init__.py +0 -0
  46. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/commands/learn.py +0 -0
  47. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/commands/quick.py +0 -0
  48. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/core/__init__.py +0 -0
  49. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/core/auth.py +0 -0
  50. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/core/client.py +0 -0
  51. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/core/csv_utils.py +0 -0
  52. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/core/errors.py +0 -0
  53. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/core/output.py +0 -0
  54. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/core/prompts.py +0 -0
  55. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/core/rest_debug.py +0 -0
  56. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/data/CLAUDE.md +0 -0
  57. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/data/__init__.py +0 -0
  58. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/data/skills/bt/SKILL.md +0 -0
  59. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/data/skills/entitle/SKILL.md +0 -0
  60. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/data/skills/epml/SKILL.md +0 -0
  61. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/data/skills/epmw/SKILL.md +0 -0
  62. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/data/skills/pra/SKILL.md +0 -0
  63. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/data/skills/pws/SKILL.md +0 -0
  64. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/entitle/__init__.py +0 -0
  65. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/entitle/client/__init__.py +0 -0
  66. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/entitle/client/base.py +0 -0
  67. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/entitle/commands/__init__.py +0 -0
  68. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/entitle/commands/accounts.py +0 -0
  69. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/entitle/commands/applications.py +0 -0
  70. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/entitle/commands/auth.py +0 -0
  71. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/entitle/commands/bundles.py +0 -0
  72. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/entitle/commands/integrations.py +0 -0
  73. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/entitle/commands/permissions.py +0 -0
  74. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/entitle/commands/policies.py +0 -0
  75. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/entitle/commands/requests.py +0 -0
  76. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/entitle/commands/resources.py +0 -0
  77. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/entitle/commands/roles.py +0 -0
  78. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/entitle/commands/users.py +0 -0
  79. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/entitle/commands/workflows.py +0 -0
  80. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/entitle/models/__init__.py +0 -0
  81. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/entitle/models/bundle.py +0 -0
  82. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/entitle/models/common.py +0 -0
  83. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/entitle/models/integration.py +0 -0
  84. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/entitle/models/permission.py +0 -0
  85. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/entitle/models/policy.py +0 -0
  86. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/entitle/models/resource.py +0 -0
  87. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/entitle/models/role.py +0 -0
  88. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/entitle/models/user.py +0 -0
  89. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/entitle/models/workflow.py +0 -0
  90. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/epml/__init__.py +0 -0
  91. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/epml/client/__init__.py +0 -0
  92. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/epml/client/base.py +0 -0
  93. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/epml/commands/__init__.py +0 -0
  94. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/epml/commands/audit.py +0 -0
  95. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/epml/commands/auth.py +0 -0
  96. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/epml/commands/client_pkg.py +0 -0
  97. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/epml/commands/clients.py +0 -0
  98. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/epml/commands/external_apis.py +0 -0
  99. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/epml/commands/hosts.py +0 -0
  100. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/epml/commands/iolog.py +0 -0
  101. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/epml/commands/license.py +0 -0
  102. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/epml/commands/quick.py +0 -0
  103. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/epml/commands/rbp_cmdgrps.py +0 -0
  104. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/epml/commands/rbp_entitlement.py +0 -0
  105. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/epml/commands/rbp_hostgrps.py +0 -0
  106. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/epml/commands/rbp_policy.py +0 -0
  107. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/epml/commands/rbp_roles.py +0 -0
  108. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/epml/commands/rbp_tests.py +0 -0
  109. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/epml/commands/rbp_tmdategrps.py +0 -0
  110. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/epml/commands/rbp_tx.py +0 -0
  111. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/epml/commands/rbp_usergrps.py +0 -0
  112. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/epml/commands/settings.py +0 -0
  113. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/epml/commands/siems.py +0 -0
  114. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/epml/commands/users.py +0 -0
  115. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/epml/models/__init__.py +0 -0
  116. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/epmw/__init__.py +0 -0
  117. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/epmw/client/__init__.py +0 -0
  118. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/epmw/client/base.py +0 -0
  119. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/epmw/commands/__init__.py +0 -0
  120. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/epmw/commands/audits.py +0 -0
  121. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/epmw/commands/auth.py +0 -0
  122. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/epmw/commands/computers.py +0 -0
  123. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/epmw/commands/events.py +0 -0
  124. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/epmw/commands/groups.py +0 -0
  125. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/epmw/commands/policies.py +0 -0
  126. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/epmw/commands/quick.py +0 -0
  127. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/epmw/commands/requests.py +0 -0
  128. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/epmw/commands/roles.py +0 -0
  129. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/epmw/commands/tasks.py +0 -0
  130. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/epmw/commands/users.py +0 -0
  131. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/epmw/models/__init__.py +0 -0
  132. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/pra/__init__.py +0 -0
  133. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/pra/client/__init__.py +0 -0
  134. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/pra/client/base.py +0 -0
  135. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/pra/commands/__init__.py +0 -0
  136. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/pra/commands/auth.py +0 -0
  137. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/pra/commands/group_policies.py +0 -0
  138. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/pra/commands/import_export.py +0 -0
  139. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/pra/commands/jump_clients.py +0 -0
  140. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/pra/commands/jump_groups.py +0 -0
  141. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/pra/commands/jump_items.py +0 -0
  142. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/pra/commands/jumpoints.py +0 -0
  143. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/pra/commands/policies.py +0 -0
  144. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/pra/commands/quick.py +0 -0
  145. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/pra/commands/teams.py +0 -0
  146. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/pra/commands/users.py +0 -0
  147. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/pra/commands/vault.py +0 -0
  148. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/pra/models/__init__.py +0 -0
  149. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/pra/models/common.py +0 -0
  150. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/pra/models/group_policy.py +0 -0
  151. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/pra/models/jump_client.py +0 -0
  152. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/pra/models/jump_group.py +0 -0
  153. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/pra/models/jump_item.py +0 -0
  154. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/pra/models/jumpoint.py +0 -0
  155. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/pra/models/team.py +0 -0
  156. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/pra/models/user.py +0 -0
  157. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/pra/models/vault.py +0 -0
  158. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/pws/__init__.py +0 -0
  159. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/pws/client/__init__.py +0 -0
  160. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/pws/client/base.py +0 -0
  161. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/pws/client/beyondinsight.py +0 -0
  162. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/pws/client/passwordsafe.py +0 -0
  163. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/pws/commands/__init__.py +0 -0
  164. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/pws/commands/accounts.py +0 -0
  165. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/pws/commands/assets.py +0 -0
  166. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/pws/commands/attributes.py +0 -0
  167. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/pws/commands/auth.py +0 -0
  168. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/pws/commands/clouds.py +0 -0
  169. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/pws/commands/config.py +0 -0
  170. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/pws/commands/credentials.py +0 -0
  171. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/pws/commands/databases.py +0 -0
  172. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/pws/commands/directories.py +0 -0
  173. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/pws/commands/functional.py +0 -0
  174. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/pws/commands/import_export.py +0 -0
  175. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/pws/commands/platforms.py +0 -0
  176. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/pws/commands/quick.py +0 -0
  177. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/pws/commands/search.py +0 -0
  178. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/pws/commands/secrets.py +0 -0
  179. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/pws/commands/systems.py +0 -0
  180. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/pws/commands/users.py +0 -0
  181. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/pws/commands/workgroups.py +0 -0
  182. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/pws/config.py +0 -0
  183. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/pws/models/__init__.py +0 -0
  184. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/pws/models/account.py +0 -0
  185. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/pws/models/asset.py +0 -0
  186. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/pws/models/common.py +0 -0
  187. {bt_cli-0.4.44 → bt_cli-0.4.46}/src/bt_cli/pws/models/system.py +0 -0
  188. {bt_cli-0.4.44 → bt_cli-0.4.46}/tests/__init__.py +0 -0
  189. {bt_cli-0.4.44 → bt_cli-0.4.46}/tests/conftest.py +0 -0
  190. {bt_cli-0.4.44 → bt_cli-0.4.46}/tests/core/__init__.py +0 -0
  191. {bt_cli-0.4.44 → bt_cli-0.4.46}/tests/core/test_auth.py +0 -0
  192. {bt_cli-0.4.44 → bt_cli-0.4.46}/tests/core/test_config.py +0 -0
  193. {bt_cli-0.4.44 → bt_cli-0.4.46}/tests/core/test_errors.py +0 -0
  194. {bt_cli-0.4.44 → bt_cli-0.4.46}/tests/core/test_rest_debug.py +0 -0
  195. {bt_cli-0.4.44 → bt_cli-0.4.46}/tests/entitle/__init__.py +0 -0
  196. {bt_cli-0.4.44 → bt_cli-0.4.46}/tests/entitle/test_client.py +0 -0
  197. {bt_cli-0.4.44 → bt_cli-0.4.46}/tests/entitle/test_commands.py +0 -0
  198. {bt_cli-0.4.44 → bt_cli-0.4.46}/tests/entitle-smoke-test.sh +0 -0
  199. {bt_cli-0.4.44 → bt_cli-0.4.46}/tests/epml/__init__.py +0 -0
  200. {bt_cli-0.4.44 → bt_cli-0.4.46}/tests/epml/test_client.py +0 -0
  201. {bt_cli-0.4.44 → bt_cli-0.4.46}/tests/epml/test_commands.py +0 -0
  202. {bt_cli-0.4.44 → bt_cli-0.4.46}/tests/epmw/__init__.py +0 -0
  203. {bt_cli-0.4.44 → bt_cli-0.4.46}/tests/epmw/test_client.py +0 -0
  204. {bt_cli-0.4.44 → bt_cli-0.4.46}/tests/epmw/test_commands.py +0 -0
  205. {bt_cli-0.4.44 → bt_cli-0.4.46}/tests/epmw-quick-test-plan.md +0 -0
  206. {bt_cli-0.4.44 → bt_cli-0.4.46}/tests/fixtures/__init__.py +0 -0
  207. {bt_cli-0.4.44 → bt_cli-0.4.46}/tests/fixtures/responses.py +0 -0
  208. {bt_cli-0.4.44 → bt_cli-0.4.46}/tests/integration/__init__.py +0 -0
  209. {bt_cli-0.4.44 → bt_cli-0.4.46}/tests/integration/conftest.py +0 -0
  210. {bt_cli-0.4.44 → bt_cli-0.4.46}/tests/integration/helpers.py +0 -0
  211. {bt_cli-0.4.44 → bt_cli-0.4.46}/tests/integration/test_entitle_integration.py +0 -0
  212. {bt_cli-0.4.44 → bt_cli-0.4.46}/tests/integration/test_epmw_integration.py +0 -0
  213. {bt_cli-0.4.44 → bt_cli-0.4.46}/tests/integration/test_epmw_lifecycle.py +0 -0
  214. {bt_cli-0.4.44 → bt_cli-0.4.46}/tests/integration/test_pra_integration.py +0 -0
  215. {bt_cli-0.4.44 → bt_cli-0.4.46}/tests/integration/test_pra_lifecycle.py +0 -0
  216. {bt_cli-0.4.44 → bt_cli-0.4.46}/tests/integration/test_pws_integration.py +0 -0
  217. {bt_cli-0.4.44 → bt_cli-0.4.46}/tests/integration/test_pws_lifecycle.py +0 -0
  218. {bt_cli-0.4.44 → bt_cli-0.4.46}/tests/pra/__init__.py +0 -0
  219. {bt_cli-0.4.44 → bt_cli-0.4.46}/tests/pra/test_client.py +0 -0
  220. {bt_cli-0.4.44 → bt_cli-0.4.46}/tests/pra/test_commands.py +0 -0
  221. {bt_cli-0.4.44 → bt_cli-0.4.46}/tests/pra-smoke-test.sh +0 -0
  222. {bt_cli-0.4.44 → bt_cli-0.4.46}/tests/pra-test-plan.md +0 -0
  223. {bt_cli-0.4.44 → bt_cli-0.4.46}/tests/pws/__init__.py +0 -0
  224. {bt_cli-0.4.44 → bt_cli-0.4.46}/tests/pws/test_client.py +0 -0
  225. {bt_cli-0.4.44 → bt_cli-0.4.46}/tests/pws/test_commands.py +0 -0
  226. {bt_cli-0.4.44 → bt_cli-0.4.46}/tests/pws-quick-test-plan.md +0 -0
  227. {bt_cli-0.4.44 → bt_cli-0.4.46}/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.44**
3
+ BeyondTrust Platform CLI for Password Safe, Entitle, PRA, EPM Windows, EPM Linux, and the BeyondTrust Secrets API. **Version: 0.4.46**
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.44
3
+ Version: 0.4.46
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
125
+ bt secrets dynamic list -p lab/aws -r # browse dynamic-secret configs
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
78
+ bt secrets dynamic list -p lab/aws -r # browse dynamic-secret configs
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.44"
7
+ version = "0.4.46"
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.44"
3
+ __version__ = "0.4.46"
@@ -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()
@@ -43,6 +43,11 @@ def configure_callback(
43
43
  client_id: Optional[str] = typer.Option(None, "--client-id", help="OAuth Client ID"),
44
44
  client_secret: Optional[str] = typer.Option(None, "--client-secret", help="OAuth Client Secret"),
45
45
  api_key: Optional[str] = typer.Option(None, "--api-key", help="API Key"),
46
+ user_api_key: Optional[str] = typer.Option(
47
+ None,
48
+ "--user-api-key",
49
+ help="Entitle user-context API key (only required for `bt entitle requests create`)",
50
+ ),
46
51
  ) -> None:
47
52
  """Configure bt-cli interactively or via flags.
48
53
 
@@ -65,11 +70,13 @@ def configure_callback(
65
70
  return
66
71
 
67
72
  # Check if any non-interactive flags were provided
68
- has_flags = any([api_url, client_id, client_secret, api_key])
73
+ has_flags = any([api_url, client_id, client_secret, api_key, user_api_key])
69
74
 
70
75
  if has_flags and product:
71
76
  # Non-interactive mode with flags
72
- _configure_with_flags(product, profile, api_url, client_id, client_secret, api_key)
77
+ _configure_with_flags(
78
+ product, profile, api_url, client_id, client_secret, api_key, user_api_key
79
+ )
73
80
  else:
74
81
  # Interactive mode
75
82
  _configure_interactive(product, profile)
@@ -219,6 +226,7 @@ def _configure_with_flags(
219
226
  client_id: Optional[str],
220
227
  client_secret: Optional[str],
221
228
  api_key: Optional[str],
229
+ user_api_key: Optional[str] = None,
222
230
  ) -> None:
223
231
  """Configure using command-line flags (non-interactive)."""
224
232
  if product not in PRODUCTS:
@@ -240,6 +248,11 @@ def _configure_with_flags(
240
248
  new_config["client_secret"] = client_secret
241
249
  if api_key:
242
250
  new_config["api_key"] = api_key
251
+ if user_api_key is not None:
252
+ if product != "entitle":
253
+ print_error("--user-api-key is only valid for product=entitle")
254
+ raise typer.Exit(2)
255
+ new_config["user_api_key"] = user_api_key
243
256
 
244
257
  # Infer auth method
245
258
  if api_key:
@@ -481,6 +494,7 @@ def import_from_env(
481
494
  "entitle": {
482
495
  "api_url": "BT_ENTITLE_API_URL",
483
496
  "api_key": "BT_ENTITLE_API_KEY",
497
+ "user_api_key": "BT_ENTITLE_USER_API_KEY",
484
498
  "verify_ssl": "BT_ENTITLE_VERIFY_SSL",
485
499
  "timeout": "BT_ENTITLE_TIMEOUT",
486
500
  },
@@ -582,6 +596,7 @@ def show_effective_config(
582
596
  "Entitle": {
583
597
  "api_url": "BT_ENTITLE_API_URL",
584
598
  "api_key": "BT_ENTITLE_API_KEY",
599
+ "user_api_key": "BT_ENTITLE_USER_API_KEY",
585
600
  "verify_ssl": "BT_ENTITLE_VERIFY_SSL",
586
601
  "timeout": "BT_ENTITLE_TIMEOUT",
587
602
  },
@@ -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())
@@ -51,7 +51,16 @@ PRODUCTS = {
51
51
  "default": "https://api.us.entitle.io",
52
52
  "example": "https://api.us.entitle.io or https://api.eu.entitle.io",
53
53
  },
54
- "api_key": {"prompt": "API Key", "required": True, "secret": True},
54
+ "api_key": {
55
+ "prompt": "Org/Admin API Key (read + admin writes)",
56
+ "required": True,
57
+ "secret": True,
58
+ },
59
+ "user_api_key": {
60
+ "prompt": "User-context API Key (only for `requests create`; leave blank to skip)",
61
+ "required": False,
62
+ "secret": True,
63
+ },
55
64
  "verify_ssl": {"prompt": "Verify SSL", "required": False, "secret": False, "default": True},
56
65
  "timeout": {"prompt": "Timeout (seconds)", "required": False, "secret": False, "default": 30},
57
66
  },
@@ -86,6 +95,33 @@ PRODUCTS = {
86
95
  "timeout": {"prompt": "Timeout (seconds)", "required": False, "secret": False, "default": 30},
87
96
  },
88
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
+ },
89
125
  "epml": {
90
126
  "name": "EPM Linux",
91
127
  "fields": {
@@ -319,6 +355,7 @@ def _get_env_prefix(product: str) -> str:
319
355
  "pra": "BT_PRA",
320
356
  "epmw": "BT_EPM",
321
357
  "epml": "BT_EPML",
358
+ "secrets": "BT_SECRETS",
322
359
  }
323
360
  return prefixes.get(product, f"BT_{product.upper()}")
324
361
 
@@ -341,6 +378,8 @@ def _get_env_mappings(product: str) -> dict[str, str]:
341
378
  if product == "pws":
342
379
  mappings["run_as"] = f"{prefix}_RUN_AS"
343
380
  mappings["api_version"] = f"{prefix}_API_VERSION"
381
+ elif product == "entitle":
382
+ mappings["user_api_key"] = f"{prefix}_USER_API_KEY"
344
383
  elif product == "epml":
345
384
  mappings["site_id"] = f"{prefix}_SITE_ID"
346
385
  mappings["pat"] = f"{prefix}_PAT"
@@ -348,6 +387,13 @@ def _get_env_mappings(product: str) -> dict[str, str]:
348
387
  # EPM-L doesn't use api_key/client_id/client_secret — drop the inherited mappings
349
388
  for unused in ("api_key", "client_id", "client_secret"):
350
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)
351
397
 
352
398
  return mappings
353
399