bt-cli 0.4.49__tar.gz → 0.4.50__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.49 → bt_cli-0.4.50}/CLAUDE.md +2 -2
  2. {bt_cli-0.4.49 → bt_cli-0.4.50}/PKG-INFO +1 -1
  3. {bt_cli-0.4.49 → bt_cli-0.4.50}/pyproject.toml +1 -1
  4. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/__init__.py +1 -1
  5. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/core/config.py +36 -6
  6. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/core/config_file.py +1 -1
  7. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/data/skills/secrets/SKILL.md +9 -4
  8. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/secrets/client/base.py +5 -3
  9. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/secrets/commands/auth.py +1 -1
  10. {bt_cli-0.4.49 → bt_cli-0.4.50}/tests/secrets/test_client.py +28 -4
  11. {bt_cli-0.4.49 → bt_cli-0.4.50}/tests/secrets/test_commands.py +2 -2
  12. {bt_cli-0.4.49 → bt_cli-0.4.50}/.claude/skills/bt/SKILL.md +0 -0
  13. {bt_cli-0.4.49 → bt_cli-0.4.50}/.claude/skills/entitle/SKILL.md +0 -0
  14. {bt_cli-0.4.49 → bt_cli-0.4.50}/.claude/skills/epml/SKILL.md +0 -0
  15. {bt_cli-0.4.49 → bt_cli-0.4.50}/.claude/skills/epmw/SKILL.md +0 -0
  16. {bt_cli-0.4.49 → bt_cli-0.4.50}/.claude/skills/pra/SKILL.md +0 -0
  17. {bt_cli-0.4.49 → bt_cli-0.4.50}/.claude/skills/pws/SKILL.md +0 -0
  18. {bt_cli-0.4.49 → bt_cli-0.4.50}/.env.example +0 -0
  19. {bt_cli-0.4.49 → bt_cli-0.4.50}/.github/workflows/ci.yml +0 -0
  20. {bt_cli-0.4.49 → bt_cli-0.4.50}/.github/workflows/release.yml +0 -0
  21. {bt_cli-0.4.49 → bt_cli-0.4.50}/.gitignore +0 -0
  22. {bt_cli-0.4.49 → bt_cli-0.4.50}/README.md +0 -0
  23. {bt_cli-0.4.49 → bt_cli-0.4.50}/assets/cli-help.png +0 -0
  24. {bt_cli-0.4.49 → bt_cli-0.4.50}/assets/cli-output.png +0 -0
  25. {bt_cli-0.4.49 → bt_cli-0.4.50}/bt-cli.spec +0 -0
  26. {bt_cli-0.4.49 → bt_cli-0.4.50}/bt_entry.py +0 -0
  27. {bt_cli-0.4.49 → bt_cli-0.4.50}/epml-clients-server-side-filters-plan.md +0 -0
  28. {bt_cli-0.4.49 → bt_cli-0.4.50}/epml-implementation-plan.md +0 -0
  29. {bt_cli-0.4.49 → bt_cli-0.4.50}/pf-implementation-plan.md +0 -0
  30. {bt_cli-0.4.49 → bt_cli-0.4.50}/scripts/bt_entry.py +0 -0
  31. {bt_cli-0.4.49 → bt_cli-0.4.50}/scripts/pf_onboard.py +0 -0
  32. {bt_cli-0.4.49 → bt_cli-0.4.50}/scripts/sync-package-data.sh +0 -0
  33. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/cli.py +0 -0
  34. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/commands/__init__.py +0 -0
  35. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/commands/configure.py +0 -0
  36. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/commands/learn.py +0 -0
  37. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/commands/quick.py +0 -0
  38. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/core/__init__.py +0 -0
  39. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/core/auth.py +0 -0
  40. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/core/client.py +0 -0
  41. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/core/csv_utils.py +0 -0
  42. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/core/errors.py +0 -0
  43. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/core/output.py +0 -0
  44. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/core/prompts.py +0 -0
  45. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/core/rest_debug.py +0 -0
  46. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/data/CLAUDE.md +0 -0
  47. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/data/__init__.py +0 -0
  48. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/data/skills/bt/SKILL.md +0 -0
  49. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/data/skills/entitle/SKILL.md +0 -0
  50. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/data/skills/epml/SKILL.md +0 -0
  51. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/data/skills/epmw/SKILL.md +0 -0
  52. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/data/skills/pra/SKILL.md +0 -0
  53. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/data/skills/pws/SKILL.md +0 -0
  54. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/entitle/__init__.py +0 -0
  55. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/entitle/client/__init__.py +0 -0
  56. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/entitle/client/base.py +0 -0
  57. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/entitle/commands/__init__.py +0 -0
  58. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/entitle/commands/accounts.py +0 -0
  59. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/entitle/commands/applications.py +0 -0
  60. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/entitle/commands/auth.py +0 -0
  61. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/entitle/commands/bundles.py +0 -0
  62. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/entitle/commands/integrations.py +0 -0
  63. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/entitle/commands/permissions.py +0 -0
  64. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/entitle/commands/policies.py +0 -0
  65. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/entitle/commands/requests.py +0 -0
  66. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/entitle/commands/resources.py +0 -0
  67. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/entitle/commands/roles.py +0 -0
  68. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/entitle/commands/users.py +0 -0
  69. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/entitle/commands/workflows.py +0 -0
  70. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/entitle/models/__init__.py +0 -0
  71. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/entitle/models/bundle.py +0 -0
  72. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/entitle/models/common.py +0 -0
  73. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/entitle/models/integration.py +0 -0
  74. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/entitle/models/permission.py +0 -0
  75. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/entitle/models/policy.py +0 -0
  76. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/entitle/models/resource.py +0 -0
  77. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/entitle/models/role.py +0 -0
  78. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/entitle/models/user.py +0 -0
  79. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/entitle/models/workflow.py +0 -0
  80. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/epml/__init__.py +0 -0
  81. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/epml/client/__init__.py +0 -0
  82. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/epml/client/base.py +0 -0
  83. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/epml/commands/__init__.py +0 -0
  84. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/epml/commands/audit.py +0 -0
  85. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/epml/commands/auth.py +0 -0
  86. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/epml/commands/client_pkg.py +0 -0
  87. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/epml/commands/clients.py +0 -0
  88. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/epml/commands/external_apis.py +0 -0
  89. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/epml/commands/hosts.py +0 -0
  90. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/epml/commands/iolog.py +0 -0
  91. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/epml/commands/license.py +0 -0
  92. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/epml/commands/quick.py +0 -0
  93. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/epml/commands/rbp_cmdgrps.py +0 -0
  94. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/epml/commands/rbp_entitlement.py +0 -0
  95. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/epml/commands/rbp_hostgrps.py +0 -0
  96. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/epml/commands/rbp_policy.py +0 -0
  97. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/epml/commands/rbp_roles.py +0 -0
  98. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/epml/commands/rbp_tests.py +0 -0
  99. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/epml/commands/rbp_tmdategrps.py +0 -0
  100. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/epml/commands/rbp_tx.py +0 -0
  101. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/epml/commands/rbp_usergrps.py +0 -0
  102. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/epml/commands/settings.py +0 -0
  103. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/epml/commands/siems.py +0 -0
  104. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/epml/commands/users.py +0 -0
  105. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/epml/models/__init__.py +0 -0
  106. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/epmw/__init__.py +0 -0
  107. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/epmw/client/__init__.py +0 -0
  108. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/epmw/client/base.py +0 -0
  109. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/epmw/commands/__init__.py +0 -0
  110. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/epmw/commands/audits.py +0 -0
  111. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/epmw/commands/auth.py +0 -0
  112. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/epmw/commands/computers.py +0 -0
  113. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/epmw/commands/events.py +0 -0
  114. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/epmw/commands/groups.py +0 -0
  115. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/epmw/commands/policies.py +0 -0
  116. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/epmw/commands/quick.py +0 -0
  117. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/epmw/commands/requests.py +0 -0
  118. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/epmw/commands/roles.py +0 -0
  119. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/epmw/commands/tasks.py +0 -0
  120. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/epmw/commands/users.py +0 -0
  121. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/epmw/models/__init__.py +0 -0
  122. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/pra/__init__.py +0 -0
  123. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/pra/client/__init__.py +0 -0
  124. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/pra/client/base.py +0 -0
  125. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/pra/commands/__init__.py +0 -0
  126. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/pra/commands/auth.py +0 -0
  127. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/pra/commands/group_policies.py +0 -0
  128. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/pra/commands/import_export.py +0 -0
  129. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/pra/commands/jump_clients.py +0 -0
  130. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/pra/commands/jump_groups.py +0 -0
  131. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/pra/commands/jump_items.py +0 -0
  132. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/pra/commands/jumpoints.py +0 -0
  133. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/pra/commands/policies.py +0 -0
  134. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/pra/commands/quick.py +0 -0
  135. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/pra/commands/teams.py +0 -0
  136. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/pra/commands/users.py +0 -0
  137. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/pra/commands/vault.py +0 -0
  138. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/pra/models/__init__.py +0 -0
  139. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/pra/models/common.py +0 -0
  140. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/pra/models/group_policy.py +0 -0
  141. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/pra/models/jump_client.py +0 -0
  142. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/pra/models/jump_group.py +0 -0
  143. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/pra/models/jump_item.py +0 -0
  144. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/pra/models/jumpoint.py +0 -0
  145. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/pra/models/team.py +0 -0
  146. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/pra/models/user.py +0 -0
  147. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/pra/models/vault.py +0 -0
  148. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/pws/__init__.py +0 -0
  149. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/pws/client/__init__.py +0 -0
  150. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/pws/client/base.py +0 -0
  151. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/pws/client/beyondinsight.py +0 -0
  152. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/pws/client/passwordsafe.py +0 -0
  153. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/pws/commands/__init__.py +0 -0
  154. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/pws/commands/accounts.py +0 -0
  155. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/pws/commands/assets.py +0 -0
  156. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/pws/commands/attributes.py +0 -0
  157. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/pws/commands/auth.py +0 -0
  158. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/pws/commands/clouds.py +0 -0
  159. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/pws/commands/config.py +0 -0
  160. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/pws/commands/credentials.py +0 -0
  161. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/pws/commands/databases.py +0 -0
  162. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/pws/commands/directories.py +0 -0
  163. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/pws/commands/functional.py +0 -0
  164. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/pws/commands/import_export.py +0 -0
  165. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/pws/commands/platforms.py +0 -0
  166. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/pws/commands/quick.py +0 -0
  167. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/pws/commands/search.py +0 -0
  168. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/pws/commands/secrets.py +0 -0
  169. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/pws/commands/systems.py +0 -0
  170. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/pws/commands/users.py +0 -0
  171. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/pws/commands/workgroups.py +0 -0
  172. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/pws/config.py +0 -0
  173. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/pws/models/__init__.py +0 -0
  174. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/pws/models/account.py +0 -0
  175. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/pws/models/asset.py +0 -0
  176. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/pws/models/common.py +0 -0
  177. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/pws/models/system.py +0 -0
  178. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/secrets/__init__.py +0 -0
  179. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/secrets/client/__init__.py +0 -0
  180. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/secrets/commands/__init__.py +0 -0
  181. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/secrets/commands/_hints.py +0 -0
  182. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/secrets/commands/dynamic.py +0 -0
  183. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/secrets/commands/folders.py +0 -0
  184. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/secrets/commands/integrations.py +0 -0
  185. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/secrets/commands/leases.py +0 -0
  186. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/secrets/commands/static.py +0 -0
  187. {bt_cli-0.4.49 → bt_cli-0.4.50}/src/bt_cli/secrets/models/__init__.py +0 -0
  188. {bt_cli-0.4.49 → bt_cli-0.4.50}/tests/__init__.py +0 -0
  189. {bt_cli-0.4.49 → bt_cli-0.4.50}/tests/conftest.py +0 -0
  190. {bt_cli-0.4.49 → bt_cli-0.4.50}/tests/core/__init__.py +0 -0
  191. {bt_cli-0.4.49 → bt_cli-0.4.50}/tests/core/test_auth.py +0 -0
  192. {bt_cli-0.4.49 → bt_cli-0.4.50}/tests/core/test_config.py +0 -0
  193. {bt_cli-0.4.49 → bt_cli-0.4.50}/tests/core/test_errors.py +0 -0
  194. {bt_cli-0.4.49 → bt_cli-0.4.50}/tests/core/test_rest_debug.py +0 -0
  195. {bt_cli-0.4.49 → bt_cli-0.4.50}/tests/entitle/__init__.py +0 -0
  196. {bt_cli-0.4.49 → bt_cli-0.4.50}/tests/entitle/test_client.py +0 -0
  197. {bt_cli-0.4.49 → bt_cli-0.4.50}/tests/entitle/test_commands.py +0 -0
  198. {bt_cli-0.4.49 → bt_cli-0.4.50}/tests/entitle-smoke-test.sh +0 -0
  199. {bt_cli-0.4.49 → bt_cli-0.4.50}/tests/epml/__init__.py +0 -0
  200. {bt_cli-0.4.49 → bt_cli-0.4.50}/tests/epml/test_client.py +0 -0
  201. {bt_cli-0.4.49 → bt_cli-0.4.50}/tests/epml/test_commands.py +0 -0
  202. {bt_cli-0.4.49 → bt_cli-0.4.50}/tests/epmw/__init__.py +0 -0
  203. {bt_cli-0.4.49 → bt_cli-0.4.50}/tests/epmw/test_client.py +0 -0
  204. {bt_cli-0.4.49 → bt_cli-0.4.50}/tests/epmw/test_commands.py +0 -0
  205. {bt_cli-0.4.49 → bt_cli-0.4.50}/tests/epmw-quick-test-plan.md +0 -0
  206. {bt_cli-0.4.49 → bt_cli-0.4.50}/tests/fixtures/__init__.py +0 -0
  207. {bt_cli-0.4.49 → bt_cli-0.4.50}/tests/fixtures/responses.py +0 -0
  208. {bt_cli-0.4.49 → bt_cli-0.4.50}/tests/integration/__init__.py +0 -0
  209. {bt_cli-0.4.49 → bt_cli-0.4.50}/tests/integration/conftest.py +0 -0
  210. {bt_cli-0.4.49 → bt_cli-0.4.50}/tests/integration/helpers.py +0 -0
  211. {bt_cli-0.4.49 → bt_cli-0.4.50}/tests/integration/test_entitle_integration.py +0 -0
  212. {bt_cli-0.4.49 → bt_cli-0.4.50}/tests/integration/test_epmw_integration.py +0 -0
  213. {bt_cli-0.4.49 → bt_cli-0.4.50}/tests/integration/test_epmw_lifecycle.py +0 -0
  214. {bt_cli-0.4.49 → bt_cli-0.4.50}/tests/integration/test_pra_integration.py +0 -0
  215. {bt_cli-0.4.49 → bt_cli-0.4.50}/tests/integration/test_pra_lifecycle.py +0 -0
  216. {bt_cli-0.4.49 → bt_cli-0.4.50}/tests/integration/test_pws_integration.py +0 -0
  217. {bt_cli-0.4.49 → bt_cli-0.4.50}/tests/integration/test_pws_lifecycle.py +0 -0
  218. {bt_cli-0.4.49 → bt_cli-0.4.50}/tests/pra/__init__.py +0 -0
  219. {bt_cli-0.4.49 → bt_cli-0.4.50}/tests/pra/test_client.py +0 -0
  220. {bt_cli-0.4.49 → bt_cli-0.4.50}/tests/pra/test_commands.py +0 -0
  221. {bt_cli-0.4.49 → bt_cli-0.4.50}/tests/pra-smoke-test.sh +0 -0
  222. {bt_cli-0.4.49 → bt_cli-0.4.50}/tests/pra-test-plan.md +0 -0
  223. {bt_cli-0.4.49 → bt_cli-0.4.50}/tests/pws/__init__.py +0 -0
  224. {bt_cli-0.4.49 → bt_cli-0.4.50}/tests/pws/test_client.py +0 -0
  225. {bt_cli-0.4.49 → bt_cli-0.4.50}/tests/pws/test_commands.py +0 -0
  226. {bt_cli-0.4.49 → bt_cli-0.4.50}/tests/pws-quick-test-plan.md +0 -0
  227. {bt_cli-0.4.49 → bt_cli-0.4.50}/tests/pws-smoke-test.sh +0 -0
  228. {bt_cli-0.4.49 → bt_cli-0.4.50}/tests/secrets/__init__.py +0 -0
@@ -1,6 +1,6 @@
1
1
  # BT-CLI
2
2
 
3
- BeyondTrust Platform CLI for Password Safe, Entitle, PRA, EPM Windows, EPM Linux, and the BeyondTrust Secrets API. **Version: 0.4.49**
3
+ BeyondTrust Platform CLI for Password Safe, Entitle, PRA, EPM Windows, EPM Linux, and the BeyondTrust Secrets API. **Version: 0.4.50**
4
4
 
5
5
  ## Setup
6
6
 
@@ -87,7 +87,7 @@ PASSWORD=$(bt pws quick checkout -s server -a admin --raw)
87
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.
88
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.
89
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`.
90
+ - **Secrets dynamic path is `/dynamic`** (NOT `/dynamic-secrets` as the doc claims); version header is `2026-04-28` (NOT 2026-01-02; server also rejects every earlier dated header — bumps are mandatory, not optional); `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
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.
92
92
 
93
93
  ## Functional vs Managed Accounts
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: bt-cli
3
- Version: 0.4.49
3
+ Version: 0.4.50
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.49"
7
+ version = "0.4.50"
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.49"
3
+ __version__ = "0.4.50"
@@ -1,6 +1,7 @@
1
1
  """Multi-product configuration management for BeyondTrust CLI."""
2
2
 
3
3
  import os
4
+ import re
4
5
  from dataclasses import dataclass, field
5
6
  from typing import Any, Optional
6
7
 
@@ -133,7 +134,7 @@ class SecretsConfig(ProductConfig):
133
134
 
134
135
  site_id: str = ""
135
136
  pat: str = ""
136
- api_version: str = "2026-02-16"
137
+ api_version: str = "2026-04-28"
137
138
 
138
139
  def validate(self) -> None:
139
140
  """Validate configuration."""
@@ -222,6 +223,31 @@ def _resolve_value(value: Any) -> Any:
222
223
  return value
223
224
 
224
225
 
226
+ _ANSI_CSI = re.compile(r"\x1b\[[0-?]*[ -/]*[@-~]")
227
+
228
+
229
+ def _strip_controls(value: Any) -> Any:
230
+ """Remove ANSI escape sequences and bare control bytes from a config string.
231
+
232
+ Why: interactive prompts and copy-paste from terminals occasionally
233
+ capture ANSI escape sequences (cursor-arrow keystrokes are the usual
234
+ culprit) into values that end up in URLs or HTTP headers. httpx and
235
+ most HTTP libraries refuse to send a URL containing non-printables and
236
+ surface a confusing "Invalid non-printable ASCII character" error
237
+ instead of the real config problem.
238
+
239
+ We strip the *whole* CSI sequence (ESC + ``[`` + params + final byte)
240
+ before the lone-control pass — otherwise the visible tail bytes like
241
+ ``[C`` and ``[D`` survive as ASCII garbage glued onto an otherwise
242
+ valid UUID, producing a clean URL that 404s instead of one the user
243
+ can see is wrong.
244
+ """
245
+ if isinstance(value, str):
246
+ cleaned = _ANSI_CSI.sub("", value)
247
+ return "".join(c for c in cleaned if c >= " " and c != "\x7f")
248
+ return value
249
+
250
+
225
251
  def _to_bool(value: Any) -> bool:
226
252
  """Convert value to boolean."""
227
253
  if isinstance(value, bool):
@@ -488,7 +514,7 @@ def load_secrets_config(env_file: Optional[str] = None, profile: Optional[str] =
488
514
  BT_SECRETS_API_URL - Gateway URL (default: https://api.beyondtrust.io)
489
515
  BT_SECRETS_SITE_ID - Site UUID (required)
490
516
  BT_SECRETS_PAT - Personal Access Token (required)
491
- BT_SECRETS_API_VERSION - API version header (default: 2026-02-16)
517
+ BT_SECRETS_API_VERSION - API version header (default: 2026-04-28)
492
518
  BT_SECRETS_VERIFY_SSL - SSL verification (default: true)
493
519
  BT_SECRETS_TIMEOUT - Request timeout in seconds (default: 30)
494
520
  """
@@ -503,11 +529,15 @@ def load_secrets_config(env_file: Optional[str] = None, profile: Optional[str] =
503
529
  if "pat" in layered:
504
530
  layered["pat"] = _resolve_value(layered["pat"])
505
531
 
532
+ # Strip ANSI/C0 control bytes from values that flow into URLs and
533
+ # headers — arrow keys captured at a `bt configure` prompt have been
534
+ # observed to leave ESC sequences in site_id, breaking every request
535
+ # with a misleading "non-printable ASCII" error from httpx.
506
536
  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"),
537
+ api_url=_strip_controls(layered.get("api_url") or os.getenv("BT_SECRETS_API_URL", "https://api.beyondtrust.io")),
538
+ site_id=_strip_controls(layered.get("site_id") or os.getenv("BT_SECRETS_SITE_ID", "")),
539
+ pat=_strip_controls(layered.get("pat") or os.getenv("BT_SECRETS_PAT", "")),
540
+ api_version=_strip_controls(layered.get("api_version") or os.getenv("BT_SECRETS_API_VERSION", "2026-04-28")),
511
541
  verify_ssl=_to_bool(layered.get("verify_ssl")) if "verify_ssl" in layered else _get_bool(os.getenv("BT_SECRETS_VERIFY_SSL")),
512
542
  timeout=_to_float(layered.get("timeout")) if "timeout" in layered else _get_float(os.getenv("BT_SECRETS_TIMEOUT"), 30.0),
513
543
  )
@@ -116,7 +116,7 @@ PRODUCTS = {
116
116
  "prompt": "API version (bt-secrets-api-version header)",
117
117
  "required": False,
118
118
  "secret": False,
119
- "default": "2026-02-16",
119
+ "default": "2026-04-28",
120
120
  },
121
121
  "verify_ssl": {"prompt": "Verify SSL", "required": False, "secret": False, "default": True},
122
122
  "timeout": {"prompt": "Timeout (seconds)", "required": False, "secret": False, "default": 30},
@@ -34,7 +34,7 @@ Set these before running commands. PAT comes from `app.beyondtrust.io`.
34
34
  export BT_SECRETS_API_URL=https://api.beyondtrust.io # default
35
35
  export BT_SECRETS_SITE_ID=<site-uuid> # required
36
36
  export BT_SECRETS_PAT=PAT_xxx # required
37
- export BT_SECRETS_API_VERSION=2026-02-16 # default; doc page is stale
37
+ export BT_SECRETS_API_VERSION=2026-04-28 # default; doc page is stale
38
38
  ```
39
39
 
40
40
  ```bash
@@ -166,8 +166,13 @@ bt secrets integrations delete my-aws --force
166
166
  - **Dynamic path is `/dynamic`, NOT `/dynamic-secrets`** — the static doc
167
167
  page hasn't caught up to the live API. The client handles this; never
168
168
  hand-hack URLs.
169
- - **API version**: header value is `bt-secrets-api-version: 2026-02-16`. The
170
- doc says `2026-01-02` and the server rejects it.
169
+ - **API version**: header value is `bt-secrets-api-version: 2026-04-28`. The
170
+ doc says `2026-01-02` and the server rejects it. The server also rejects
171
+ every earlier dated header it has retired, so an old default surfaces as
172
+ `API version not found: <date> is not a known API version` (helpfully the
173
+ error names the current version when you send one that's *newer* than
174
+ current — `is newer than current version <date>`). Periodic bumps are
175
+ mandatory; treat the default as a moving target, not a constant.
171
176
  - **POST `/dynamic/{name}/generate` returns 201**, not 200. The client
172
177
  accepts both via `raise_for_status` semantics.
173
178
  - **`/leases/revoke` is scope-gated** — `bt secrets leases revoke` exits 3
@@ -220,5 +225,5 @@ directly at the requested path (this trips up newcomers — passing
220
225
 
221
226
  - Base URL: `https://api.beyondtrust.io/site/<site-id>/secrets`
222
227
  - Auth: `Authorization: Bearer <PAT>`
223
- - Version header: `bt-secrets-api-version: 2026-02-16`
228
+ - Version header: `bt-secrets-api-version: 2026-04-28`
224
229
  - Rate limit: 500/window — respect `x-ratelimit-reset` on 429.
@@ -14,9 +14,11 @@ from bt_cli.core.rest_debug import get_event_hooks
14
14
  logger = logging.getLogger(__name__)
15
15
 
16
16
 
17
- # Deltas vs the static doc page (live as of 2026-05-13):
18
- # * Version header value is "2026-02-16" — the doc claims 2026-01-02 which
19
- # the server rejects.
17
+ # Deltas vs the static doc page (live as of 2026-05-14):
18
+ # * Version header value is "2026-04-28" — the doc claims 2026-01-02 which
19
+ # the server rejects. The server also rejects any earlier dated header,
20
+ # so a stale default surfaces as "API version not found" rather than a
21
+ # working-but-old response.
20
22
  # * The dynamic-secret path is "/dynamic" not "/dynamic-secrets".
21
23
  # * POST /dynamic/{name}/generate returns 201, not 200.
22
24
  # * /leases/revoke is scope-gated; the lab PAT returns 403. Surface that
@@ -53,7 +53,7 @@ def status() -> None:
53
53
  api_url = os.getenv("BT_SECRETS_API_URL", "https://api.beyondtrust.io")
54
54
  site_id = os.getenv("BT_SECRETS_SITE_ID", "")
55
55
  pat = os.getenv("BT_SECRETS_PAT", "")
56
- api_version = os.getenv("BT_SECRETS_API_VERSION", "2026-02-16")
56
+ api_version = os.getenv("BT_SECRETS_API_VERSION", "2026-04-28")
57
57
 
58
58
  typer.echo("Secrets API Configuration:")
59
59
  typer.echo(f" Gateway URL: {api_url}")
@@ -18,7 +18,7 @@ def secrets_config() -> SecretsConfig:
18
18
  api_url="https://mock-secrets.example.com",
19
19
  site_id=SITE,
20
20
  pat="PAT_test_xxxxxxxxxxxxxxxxxxxxxxxxxx",
21
- api_version="2026-02-16",
21
+ api_version="2026-04-28",
22
22
  verify_ssl=False,
23
23
  timeout=5.0,
24
24
  )
@@ -58,7 +58,7 @@ class TestHeadersAndURL:
58
58
  def test_base_url_trailing_slash_normalized(self):
59
59
  cfg = SecretsConfig(
60
60
  api_url="https://mock-secrets.example.com/",
61
- site_id=SITE, pat="PAT", api_version="2026-02-16", verify_ssl=False,
61
+ site_id=SITE, pat="PAT", api_version="2026-04-28", verify_ssl=False,
62
62
  )
63
63
  c = SecretsClient(cfg)
64
64
  assert c._url("/folders") == f"{BASE}/folders"
@@ -66,7 +66,7 @@ class TestHeadersAndURL:
66
66
  def test_headers_include_bearer_and_version(self, secrets_config):
67
67
  h = SecretsClient(secrets_config)._headers()
68
68
  assert h["Authorization"] == "Bearer PAT_test_xxxxxxxxxxxxxxxxxxxxxxxxxx"
69
- assert h["bt-secrets-api-version"] == "2026-02-16"
69
+ assert h["bt-secrets-api-version"] == "2026-04-28"
70
70
  assert h["Accept"] == "application/json"
71
71
 
72
72
  @respx.mock
@@ -77,7 +77,7 @@ class TestHeadersAndURL:
77
77
  SecretsClient(secrets_config).list_folders()
78
78
  req = route.calls[0].request
79
79
  assert req.headers["Authorization"].startswith("Bearer PAT_test")
80
- assert req.headers["bt-secrets-api-version"] == "2026-02-16"
80
+ assert req.headers["bt-secrets-api-version"] == "2026-04-28"
81
81
 
82
82
 
83
83
  # ---------------------------------------------------------------------------
@@ -261,3 +261,27 @@ class TestResponseUnwrapping:
261
261
  )
262
262
  SecretsClient(secrets_config).list_integrations(type_="aws")
263
263
  assert "type=aws" in str(route.calls[0].request.url)
264
+
265
+
266
+ # ---------------------------------------------------------------------------
267
+ # Config loader strips ANSI/C0 control bytes from URL/header-bound fields
268
+ # ---------------------------------------------------------------------------
269
+
270
+ class TestLoaderSanitization:
271
+ def test_strip_controls_removes_ansi_escapes(self, monkeypatch, tmp_path):
272
+ from bt_cli.core.config import load_secrets_config
273
+
274
+ # Pretend the user typed arrow keys at the `bt configure` prompt:
275
+ # the site_id picks up cursor-forward/back ESC sequences. Without
276
+ # the sanitizer these would land in the URL and httpx would reject
277
+ # the request with a misleading non-printable-ASCII error.
278
+ dirty_site = "7f735e1b-5ecb-49fa-87e8-eddf54a9c745\x1b[C\x1b[D"
279
+ monkeypatch.setenv("BT_SECRETS_SITE_ID", dirty_site)
280
+ monkeypatch.setenv("BT_SECRETS_PAT", "PAT_x")
281
+ monkeypatch.setenv("BT_SECRETS_API_URL", "https://api.example.com")
282
+ # Isolate from any real ~/.bt-cli/config.yaml on the dev box.
283
+ monkeypatch.setenv("HOME", str(tmp_path))
284
+
285
+ cfg = load_secrets_config()
286
+ assert cfg.site_id == "7f735e1b-5ecb-49fa-87e8-eddf54a9c745"
287
+ assert "\x1b" not in cfg.site_id
@@ -20,7 +20,7 @@ def env_overrides(monkeypatch):
20
20
  monkeypatch.setenv("BT_SECRETS_API_URL", "https://mock-secrets.example.com")
21
21
  monkeypatch.setenv("BT_SECRETS_SITE_ID", SITE)
22
22
  monkeypatch.setenv("BT_SECRETS_PAT", "PAT_test_xxxxxxxxxxxxxxxxxxxxxxxxxx")
23
- monkeypatch.setenv("BT_SECRETS_API_VERSION", "2026-02-16")
23
+ monkeypatch.setenv("BT_SECRETS_API_VERSION", "2026-04-28")
24
24
  monkeypatch.setenv("BT_SECRETS_VERIFY_SSL", "false")
25
25
 
26
26
 
@@ -33,7 +33,7 @@ class TestAuth:
33
33
  result = CliRunner().invoke(secrets_app, ["auth", "test"])
34
34
  assert result.exit_code == 0
35
35
  assert "Connected to Secrets API" in result.stdout
36
- assert "2026-02-16" in result.stdout
36
+ assert "2026-04-28" in result.stdout
37
37
 
38
38
  @respx.mock
39
39
  def test_auth_test_401_explains_pat(self, env_overrides):
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
File without changes
File without changes