bt-cli 0.4.73__tar.gz → 0.6.0__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 (324) hide show
  1. {bt_cli-0.4.73/src/bt_cli/data → bt_cli-0.6.0/.claude}/skills/entitle/SKILL.md +166 -3
  2. bt_cli-0.6.0/.claude/skills/pf/SKILL.md +281 -0
  3. {bt_cli-0.4.73/src/bt_cli/data → bt_cli-0.6.0/.claude}/skills/pra/SKILL.md +3 -0
  4. bt_cli-0.6.0/.claude/skills/secrets/SKILL.md +19 -0
  5. bt_cli-0.6.0/.claude/skills/wlc/SKILL.md +452 -0
  6. {bt_cli-0.4.73 → bt_cli-0.6.0}/CLAUDE.md +20 -14
  7. {bt_cli-0.4.73 → bt_cli-0.6.0}/PKG-INFO +59 -18
  8. {bt_cli-0.4.73 → bt_cli-0.6.0}/README.md +58 -17
  9. {bt_cli-0.4.73 → bt_cli-0.6.0}/bt-cli-cheatsheet.html +44 -0
  10. bt_cli-0.6.0/entitle-gaps-implementation-plan.md +378 -0
  11. {bt_cli-0.4.73 → bt_cli-0.6.0}/pyproject.toml +1 -1
  12. bt_cli-0.6.0/scripts/sync-package-data.sh +77 -0
  13. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/__init__.py +1 -1
  14. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/cli.py +50 -26
  15. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/commands/configure.py +15 -15
  16. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/commands/learn.py +2 -2
  17. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/commands/quick.py +5 -6
  18. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/core/config.py +68 -30
  19. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/core/config_file.py +36 -4
  20. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/core/output.py +57 -4
  21. bt_cli-0.6.0/src/bt_cli/data/CLAUDE.md +146 -0
  22. bt_cli-0.6.0/src/bt_cli/data/skills/entitle/SKILL.md +468 -0
  23. {bt_cli-0.4.73/.claude → bt_cli-0.6.0/src/bt_cli/data}/skills/epml/SKILL.md +24 -3
  24. bt_cli-0.6.0/src/bt_cli/data/skills/pf/SKILL.md +281 -0
  25. bt_cli-0.6.0/src/bt_cli/data/skills/pra/SKILL.md +219 -0
  26. bt_cli-0.6.0/src/bt_cli/data/skills/pws/SKILL.md +353 -0
  27. bt_cli-0.6.0/src/bt_cli/data/skills/secrets/SKILL.md +19 -0
  28. bt_cli-0.6.0/src/bt_cli/data/skills/session-analysis/SKILL.md +72 -0
  29. bt_cli-0.6.0/src/bt_cli/data/skills/wlc/SKILL.md +452 -0
  30. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/entitle/client/base.py +358 -19
  31. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/entitle/commands/__init__.py +17 -0
  32. bt_cli-0.6.0/src/bt_cli/entitle/commands/_helpers.py +68 -0
  33. bt_cli-0.6.0/src/bt_cli/entitle/commands/agent_tokens.py +402 -0
  34. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/entitle/commands/auth.py +4 -2
  35. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/entitle/commands/bundles.py +65 -4
  36. bt_cli-0.6.0/src/bt_cli/entitle/commands/chats.py +162 -0
  37. bt_cli-0.6.0/src/bt_cli/entitle/commands/directory_groups.py +84 -0
  38. bt_cli-0.6.0/src/bt_cli/entitle/commands/forwards.py +392 -0
  39. bt_cli-0.6.0/src/bt_cli/entitle/commands/integrations.py +421 -0
  40. bt_cli-0.6.0/src/bt_cli/entitle/commands/permissions.py +139 -0
  41. bt_cli-0.6.0/src/bt_cli/entitle/commands/policies.py +336 -0
  42. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/entitle/commands/requests.py +22 -10
  43. bt_cli-0.6.0/src/bt_cli/entitle/commands/resources.py +279 -0
  44. bt_cli-0.6.0/src/bt_cli/entitle/commands/roles.py +368 -0
  45. bt_cli-0.6.0/src/bt_cli/entitle/commands/tasks.py +281 -0
  46. bt_cli-0.6.0/src/bt_cli/entitle/commands/user_accounts.py +209 -0
  47. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/entitle/commands/users.py +81 -5
  48. bt_cli-0.6.0/src/bt_cli/entitle/commands/webhooks.py +516 -0
  49. bt_cli-0.6.0/src/bt_cli/entitle/commands/workflow_webhook_writes.py +304 -0
  50. bt_cli-0.6.0/src/bt_cli/entitle/commands/workflows.py +712 -0
  51. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/epml/commands/client_pkg.py +5 -0
  52. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/epml/commands/rbp_roles.py +4 -8
  53. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/epml/commands/rbp_tmdategrps.py +5 -0
  54. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/epml/commands/rbp_usergrps.py +5 -0
  55. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/epml/commands/settings.py +5 -0
  56. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/epmw/commands/groups.py +5 -0
  57. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/epmw/commands/policies.py +16 -9
  58. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/epmw/commands/quick.py +16 -11
  59. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/epmw/commands/users.py +5 -0
  60. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pf/client/base.py +126 -16
  61. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pf/commands/__init__.py +6 -1
  62. bt_cli-0.6.0/src/bt_cli/pf/commands/_common.py +77 -0
  63. bt_cli-0.6.0/src/bt_cli/pf/commands/policies.py +568 -0
  64. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pf/commands/users.py +25 -62
  65. bt_cli-0.6.0/src/bt_cli/pf/commands/workload_identities.py +259 -0
  66. bt_cli-0.6.0/src/bt_cli/pf/validation.py +104 -0
  67. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pra/commands/group_policies.py +17 -0
  68. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pra/commands/import_export.py +3 -4
  69. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pra/commands/jump_items.py +20 -0
  70. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pra/commands/quick.py +9 -9
  71. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pra/commands/vault.py +10 -0
  72. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pws/commands/accounts.py +14 -5
  73. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pws/commands/assets.py +13 -4
  74. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pws/commands/clouds.py +8 -4
  75. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pws/commands/config.py +5 -6
  76. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pws/commands/credentials.py +3 -4
  77. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pws/commands/databases.py +18 -4
  78. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pws/commands/directories.py +8 -4
  79. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pws/commands/functional.py +17 -3
  80. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pws/commands/import_export.py +3 -4
  81. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pws/commands/platforms.py +3 -4
  82. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pws/commands/quick.py +9 -10
  83. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pws/commands/search.py +7 -8
  84. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pws/commands/secrets.py +51 -7
  85. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pws/commands/sessions.py +7 -6
  86. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pws/commands/systems.py +8 -4
  87. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pws/commands/users.py +9 -9
  88. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pws/commands/workgroups.py +7 -3
  89. bt_cli-0.6.0/src/bt_cli/wlc/__init__.py +1 -0
  90. bt_cli-0.6.0/src/bt_cli/wlc/client/__init__.py +5 -0
  91. {bt_cli-0.4.73/src/bt_cli/secrets → bt_cli-0.6.0/src/bt_cli/wlc}/client/base.py +54 -21
  92. bt_cli-0.6.0/src/bt_cli/wlc/commands/__init__.py +37 -0
  93. {bt_cli-0.4.73/src/bt_cli/secrets → bt_cli-0.6.0/src/bt_cli/wlc}/commands/_hints.py +1 -1
  94. bt_cli-0.6.0/src/bt_cli/wlc/commands/access.py +216 -0
  95. bt_cli-0.6.0/src/bt_cli/wlc/commands/auth.py +84 -0
  96. {bt_cli-0.4.73/src/bt_cli/secrets → bt_cli-0.6.0/src/bt_cli/wlc}/commands/dynamic.py +17 -17
  97. {bt_cli-0.4.73/src/bt_cli/secrets → bt_cli-0.6.0/src/bt_cli/wlc}/commands/folders.py +7 -7
  98. {bt_cli-0.4.73/src/bt_cli/secrets → bt_cli-0.6.0/src/bt_cli/wlc}/commands/integrations.py +6 -6
  99. {bt_cli-0.4.73/src/bt_cli/secrets → bt_cli-0.6.0/src/bt_cli/wlc}/commands/leases.py +5 -5
  100. {bt_cli-0.4.73/src/bt_cli/secrets → bt_cli-0.6.0/src/bt_cli/wlc}/commands/static.py +8 -8
  101. bt_cli-0.6.0/src/bt_cli/wlc/commands/tree.py +102 -0
  102. bt_cli-0.6.0/src/bt_cli/wlc/models/__init__.py +1 -0
  103. bt_cli-0.6.0/src/bt_cli/wlc/policy.py +654 -0
  104. bt_cli-0.6.0/src/bt_cli/wlc/snapshot.py +40 -0
  105. bt_cli-0.6.0/tests/_runner.py +21 -0
  106. {bt_cli-0.4.73 → bt_cli-0.6.0}/tests/conftest.py +44 -0
  107. {bt_cli-0.4.73 → bt_cli-0.6.0}/tests/core/test_command_tree.py +22 -0
  108. {bt_cli-0.4.73 → bt_cli-0.6.0}/tests/core/test_json_stdout_purity.py +4 -10
  109. bt_cli-0.6.0/tests/core/test_package_data_sync.py +353 -0
  110. bt_cli-0.6.0/tests/data/entitle-api-reference.openapi.json +8930 -0
  111. bt_cli-0.6.0/tests/entitle/test_agent_tokens.py +322 -0
  112. bt_cli-0.6.0/tests/entitle/test_agent_tokens_writes.py +433 -0
  113. bt_cli-0.6.0/tests/entitle/test_chats.py +189 -0
  114. {bt_cli-0.4.73 → bt_cli-0.6.0}/tests/entitle/test_client.py +59 -0
  115. {bt_cli-0.4.73 → bt_cli-0.6.0}/tests/entitle/test_commands.py +326 -3
  116. bt_cli-0.6.0/tests/entitle/test_coverage.py +123 -0
  117. bt_cli-0.6.0/tests/entitle/test_directory_groups.py +222 -0
  118. bt_cli-0.6.0/tests/entitle/test_forwards.py +193 -0
  119. bt_cli-0.6.0/tests/entitle/test_forwards_writes.py +521 -0
  120. bt_cli-0.6.0/tests/entitle/test_integrations_writes.py +466 -0
  121. bt_cli-0.6.0/tests/entitle/test_policies_writes.py +413 -0
  122. bt_cli-0.6.0/tests/entitle/test_resources_writes.py +375 -0
  123. bt_cli-0.6.0/tests/entitle/test_roles_writes.py +390 -0
  124. bt_cli-0.6.0/tests/entitle/test_tasks.py +219 -0
  125. bt_cli-0.6.0/tests/entitle/test_tasks_writes.py +337 -0
  126. bt_cli-0.6.0/tests/entitle/test_user_accounts.py +260 -0
  127. bt_cli-0.6.0/tests/entitle/test_user_accounts_writes.py +419 -0
  128. bt_cli-0.6.0/tests/entitle/test_webhooks.py +279 -0
  129. bt_cli-0.6.0/tests/entitle/test_webhooks_writes.py +590 -0
  130. bt_cli-0.6.0/tests/entitle/test_workflow_webhook_writes.py +522 -0
  131. bt_cli-0.6.0/tests/entitle/test_workflows_writes.py +508 -0
  132. bt_cli-0.6.0/tests/pf/conftest.py +21 -0
  133. {bt_cli-0.4.73 → bt_cli-0.6.0}/tests/pf/test_client.py +61 -2
  134. {bt_cli-0.4.73 → bt_cli-0.6.0}/tests/pf/test_commands.py +40 -0
  135. bt_cli-0.6.0/tests/pf/test_policies.py +485 -0
  136. bt_cli-0.6.0/tests/pf/test_workload_identities.py +244 -0
  137. {bt_cli-0.4.73 → bt_cli-0.6.0}/tests/pra/test_jump_item_fixes.py +4 -10
  138. {bt_cli-0.4.73 → bt_cli-0.6.0}/tests/pws/test_commands.py +4 -10
  139. bt_cli-0.6.0/tests/wlc/conftest.py +21 -0
  140. {bt_cli-0.4.73/tests/secrets → bt_cli-0.6.0/tests/wlc}/test_client.py +9 -7
  141. {bt_cli-0.4.73/tests/secrets → bt_cli-0.6.0/tests/wlc}/test_commands.py +27 -14
  142. bt_cli-0.6.0/tests/wlc/test_policy.py +366 -0
  143. bt_cli-0.6.0/tests/wlc/test_rename.py +170 -0
  144. bt_cli-0.6.0/tests/wlc/test_tree_access.py +244 -0
  145. bt_cli-0.4.73/.claude/skills/entitle/SKILL.md +0 -231
  146. bt_cli-0.4.73/.claude/skills/pra/SKILL.md +0 -153
  147. bt_cli-0.4.73/.claude/skills/pws/SKILL.md +0 -198
  148. bt_cli-0.4.73/scripts/sync-package-data.sh +0 -21
  149. bt_cli-0.4.73/src/bt_cli/data/CLAUDE.md +0 -118
  150. bt_cli-0.4.73/src/bt_cli/data/skills/pf/SKILL.md +0 -116
  151. bt_cli-0.4.73/src/bt_cli/data/skills/secrets/SKILL.md +0 -229
  152. bt_cli-0.4.73/src/bt_cli/entitle/commands/integrations.py +0 -60
  153. bt_cli-0.4.73/src/bt_cli/entitle/commands/permissions.py +0 -70
  154. bt_cli-0.4.73/src/bt_cli/entitle/commands/policies.py +0 -97
  155. bt_cli-0.4.73/src/bt_cli/entitle/commands/resources.py +0 -131
  156. bt_cli-0.4.73/src/bt_cli/entitle/commands/roles.py +0 -74
  157. bt_cli-0.4.73/src/bt_cli/entitle/commands/workflows.py +0 -187
  158. bt_cli-0.4.73/src/bt_cli/secrets/__init__.py +0 -1
  159. bt_cli-0.4.73/src/bt_cli/secrets/client/__init__.py +0 -5
  160. bt_cli-0.4.73/src/bt_cli/secrets/commands/__init__.py +0 -23
  161. bt_cli-0.4.73/src/bt_cli/secrets/commands/auth.py +0 -67
  162. bt_cli-0.4.73/src/bt_cli/secrets/models/__init__.py +0 -1
  163. {bt_cli-0.4.73 → bt_cli-0.6.0}/.claude/skills/bt/SKILL.md +0 -0
  164. {bt_cli-0.4.73/src/bt_cli/data → bt_cli-0.6.0/.claude}/skills/epml/SKILL.md +0 -0
  165. {bt_cli-0.4.73 → bt_cli-0.6.0}/.claude/skills/epmw/SKILL.md +0 -0
  166. {bt_cli-0.4.73/src/bt_cli/data → bt_cli-0.6.0/.claude}/skills/pws/SKILL.md +0 -0
  167. {bt_cli-0.4.73/src/bt_cli/data → bt_cli-0.6.0/.claude}/skills/session-analysis/SKILL.md +0 -0
  168. {bt_cli-0.4.73 → bt_cli-0.6.0}/.env.example +0 -0
  169. {bt_cli-0.4.73 → bt_cli-0.6.0}/.github/workflows/ci.yml +0 -0
  170. {bt_cli-0.4.73 → bt_cli-0.6.0}/.github/workflows/release.yml +0 -0
  171. {bt_cli-0.4.73 → bt_cli-0.6.0}/.gitignore +0 -0
  172. {bt_cli-0.4.73 → bt_cli-0.6.0}/assets/cli-help.png +0 -0
  173. {bt_cli-0.4.73 → bt_cli-0.6.0}/assets/cli-output.png +0 -0
  174. {bt_cli-0.4.73 → bt_cli-0.6.0}/bt-cli.spec +0 -0
  175. {bt_cli-0.4.73 → bt_cli-0.6.0}/bt_entry.py +0 -0
  176. {bt_cli-0.4.73 → bt_cli-0.6.0}/epml-clients-server-side-filters-plan.md +0 -0
  177. {bt_cli-0.4.73 → bt_cli-0.6.0}/epml-implementation-plan.md +0 -0
  178. {bt_cli-0.4.73 → bt_cli-0.6.0}/pf-implementation-plan.md +0 -0
  179. {bt_cli-0.4.73 → bt_cli-0.6.0}/scripts/bt_entry.py +0 -0
  180. {bt_cli-0.4.73 → bt_cli-0.6.0}/scripts/pf_onboard.py +0 -0
  181. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/commands/__init__.py +0 -0
  182. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/core/__init__.py +0 -0
  183. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/core/auth.py +0 -0
  184. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/core/client.py +0 -0
  185. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/core/command_tree.py +0 -0
  186. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/core/csv_utils.py +0 -0
  187. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/core/errors.py +0 -0
  188. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/core/prompts.py +0 -0
  189. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/core/rest_debug.py +0 -0
  190. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/data/__init__.py +0 -0
  191. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/data/skills/bt/SKILL.md +0 -0
  192. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/data/skills/epmw/SKILL.md +0 -0
  193. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/entitle/__init__.py +0 -0
  194. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/entitle/client/__init__.py +0 -0
  195. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/entitle/commands/accounts.py +0 -0
  196. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/entitle/commands/applications.py +0 -0
  197. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/entitle/commands/audit.py +0 -0
  198. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/entitle/commands/jit.py +0 -0
  199. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/entitle/models/__init__.py +0 -0
  200. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/entitle/models/bundle.py +0 -0
  201. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/entitle/models/common.py +0 -0
  202. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/entitle/models/integration.py +0 -0
  203. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/entitle/models/permission.py +0 -0
  204. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/entitle/models/policy.py +0 -0
  205. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/entitle/models/resource.py +0 -0
  206. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/entitle/models/role.py +0 -0
  207. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/entitle/models/user.py +0 -0
  208. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/entitle/models/workflow.py +0 -0
  209. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/epml/__init__.py +0 -0
  210. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/epml/client/__init__.py +0 -0
  211. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/epml/client/base.py +0 -0
  212. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/epml/commands/__init__.py +0 -0
  213. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/epml/commands/audit.py +0 -0
  214. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/epml/commands/auth.py +0 -0
  215. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/epml/commands/clients.py +0 -0
  216. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/epml/commands/external_apis.py +0 -0
  217. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/epml/commands/hosts.py +0 -0
  218. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/epml/commands/iolog.py +0 -0
  219. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/epml/commands/license.py +0 -0
  220. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/epml/commands/quick.py +0 -0
  221. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/epml/commands/rbp_cmdgrps.py +0 -0
  222. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/epml/commands/rbp_entitlement.py +0 -0
  223. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/epml/commands/rbp_hostgrps.py +0 -0
  224. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/epml/commands/rbp_policy.py +0 -0
  225. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/epml/commands/rbp_tests.py +0 -0
  226. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/epml/commands/rbp_tx.py +0 -0
  227. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/epml/commands/siems.py +0 -0
  228. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/epml/commands/users.py +0 -0
  229. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/epml/models/__init__.py +0 -0
  230. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/epmw/__init__.py +0 -0
  231. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/epmw/client/__init__.py +0 -0
  232. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/epmw/client/base.py +0 -0
  233. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/epmw/commands/__init__.py +0 -0
  234. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/epmw/commands/audits.py +0 -0
  235. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/epmw/commands/auth.py +0 -0
  236. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/epmw/commands/computers.py +0 -0
  237. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/epmw/commands/events.py +0 -0
  238. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/epmw/commands/requests.py +0 -0
  239. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/epmw/commands/roles.py +0 -0
  240. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/epmw/commands/tasks.py +0 -0
  241. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/epmw/models/__init__.py +0 -0
  242. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pf/__init__.py +0 -0
  243. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pf/client/__init__.py +0 -0
  244. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pf/commands/auth.py +0 -0
  245. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pf/commands/machines.py +0 -0
  246. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pf/commands/tokens.py +0 -0
  247. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pf/commands/user.py +0 -0
  248. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pra/__init__.py +0 -0
  249. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pra/client/__init__.py +0 -0
  250. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pra/client/base.py +0 -0
  251. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pra/commands/__init__.py +0 -0
  252. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pra/commands/auth.py +0 -0
  253. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pra/commands/jump_clients.py +0 -0
  254. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pra/commands/jump_groups.py +0 -0
  255. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pra/commands/jumpoints.py +0 -0
  256. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pra/commands/policies.py +0 -0
  257. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pra/commands/teams.py +0 -0
  258. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pra/commands/users.py +0 -0
  259. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pra/models/__init__.py +0 -0
  260. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pra/models/common.py +0 -0
  261. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pra/models/group_policy.py +0 -0
  262. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pra/models/jump_client.py +0 -0
  263. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pra/models/jump_group.py +0 -0
  264. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pra/models/jump_item.py +0 -0
  265. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pra/models/jumpoint.py +0 -0
  266. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pra/models/team.py +0 -0
  267. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pra/models/user.py +0 -0
  268. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pra/models/vault.py +0 -0
  269. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pws/__init__.py +0 -0
  270. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pws/client/__init__.py +0 -0
  271. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pws/client/base.py +0 -0
  272. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pws/client/beyondinsight.py +0 -0
  273. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pws/client/passwordsafe.py +0 -0
  274. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pws/commands/__init__.py +0 -0
  275. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pws/commands/attributes.py +0 -0
  276. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pws/commands/audit.py +0 -0
  277. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pws/commands/auth.py +0 -0
  278. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pws/config.py +0 -0
  279. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pws/models/__init__.py +0 -0
  280. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pws/models/account.py +0 -0
  281. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pws/models/asset.py +0 -0
  282. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pws/models/common.py +0 -0
  283. {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pws/models/system.py +0 -0
  284. {bt_cli-0.4.73 → bt_cli-0.6.0}/tests/__init__.py +0 -0
  285. {bt_cli-0.4.73 → bt_cli-0.6.0}/tests/core/__init__.py +0 -0
  286. {bt_cli-0.4.73 → bt_cli-0.6.0}/tests/core/test_auth.py +0 -0
  287. {bt_cli-0.4.73 → bt_cli-0.6.0}/tests/core/test_config.py +0 -0
  288. {bt_cli-0.4.73 → bt_cli-0.6.0}/tests/core/test_config_file.py +0 -0
  289. {bt_cli-0.4.73 → bt_cli-0.6.0}/tests/core/test_errors.py +0 -0
  290. {bt_cli-0.4.73 → bt_cli-0.6.0}/tests/core/test_output.py +0 -0
  291. {bt_cli-0.4.73 → bt_cli-0.6.0}/tests/core/test_rest_debug.py +0 -0
  292. {bt_cli-0.4.73 → bt_cli-0.6.0}/tests/entitle/__init__.py +0 -0
  293. {bt_cli-0.4.73 → bt_cli-0.6.0}/tests/entitle-smoke-test.sh +0 -0
  294. {bt_cli-0.4.73 → bt_cli-0.6.0}/tests/epml/__init__.py +0 -0
  295. {bt_cli-0.4.73 → bt_cli-0.6.0}/tests/epml/test_client.py +0 -0
  296. {bt_cli-0.4.73 → bt_cli-0.6.0}/tests/epml/test_commands.py +0 -0
  297. {bt_cli-0.4.73 → bt_cli-0.6.0}/tests/epmw/__init__.py +0 -0
  298. {bt_cli-0.4.73 → bt_cli-0.6.0}/tests/epmw/test_client.py +0 -0
  299. {bt_cli-0.4.73 → bt_cli-0.6.0}/tests/epmw/test_commands.py +0 -0
  300. {bt_cli-0.4.73 → bt_cli-0.6.0}/tests/epmw-quick-test-plan.md +0 -0
  301. {bt_cli-0.4.73 → bt_cli-0.6.0}/tests/fixtures/__init__.py +0 -0
  302. {bt_cli-0.4.73 → bt_cli-0.6.0}/tests/fixtures/responses.py +0 -0
  303. {bt_cli-0.4.73 → bt_cli-0.6.0}/tests/integration/__init__.py +0 -0
  304. {bt_cli-0.4.73 → bt_cli-0.6.0}/tests/integration/conftest.py +0 -0
  305. {bt_cli-0.4.73 → bt_cli-0.6.0}/tests/integration/helpers.py +0 -0
  306. {bt_cli-0.4.73 → bt_cli-0.6.0}/tests/integration/test_entitle_integration.py +0 -0
  307. {bt_cli-0.4.73 → bt_cli-0.6.0}/tests/integration/test_epmw_integration.py +0 -0
  308. {bt_cli-0.4.73 → bt_cli-0.6.0}/tests/integration/test_epmw_lifecycle.py +0 -0
  309. {bt_cli-0.4.73 → bt_cli-0.6.0}/tests/integration/test_pra_integration.py +0 -0
  310. {bt_cli-0.4.73 → bt_cli-0.6.0}/tests/integration/test_pra_lifecycle.py +0 -0
  311. {bt_cli-0.4.73 → bt_cli-0.6.0}/tests/integration/test_pws_integration.py +0 -0
  312. {bt_cli-0.4.73 → bt_cli-0.6.0}/tests/integration/test_pws_lifecycle.py +0 -0
  313. {bt_cli-0.4.73 → bt_cli-0.6.0}/tests/pf/__init__.py +0 -0
  314. {bt_cli-0.4.73 → bt_cli-0.6.0}/tests/pra/__init__.py +0 -0
  315. {bt_cli-0.4.73 → bt_cli-0.6.0}/tests/pra/test_client.py +0 -0
  316. {bt_cli-0.4.73 → bt_cli-0.6.0}/tests/pra/test_commands.py +0 -0
  317. {bt_cli-0.4.73 → bt_cli-0.6.0}/tests/pra-smoke-test.sh +0 -0
  318. {bt_cli-0.4.73 → bt_cli-0.6.0}/tests/pra-test-plan.md +0 -0
  319. {bt_cli-0.4.73 → bt_cli-0.6.0}/tests/pws/__init__.py +0 -0
  320. {bt_cli-0.4.73 → bt_cli-0.6.0}/tests/pws/test_client.py +0 -0
  321. {bt_cli-0.4.73 → bt_cli-0.6.0}/tests/pws/test_quick_sessions.py +0 -0
  322. {bt_cli-0.4.73 → bt_cli-0.6.0}/tests/pws-quick-test-plan.md +0 -0
  323. {bt_cli-0.4.73 → bt_cli-0.6.0}/tests/pws-smoke-test.sh +0 -0
  324. {bt_cli-0.4.73/tests/secrets → bt_cli-0.6.0/tests/wlc}/__init__.py +0 -0
@@ -5,6 +5,33 @@ description: Entitle commands for JIT access, bundles, workflows, and permission
5
5
 
6
6
  # Entitle Commands (`bt entitle`)
7
7
 
8
+ ## Two API tokens — read vs. write-on-behalf-of-user
9
+
10
+ Entitle issues two distinct kinds of bearer token. The CLI uses both:
11
+
12
+ | Token | Config field | Env var | Used for |
13
+ |---|---|---|---|
14
+ | **Org / admin** | `entitle.api_key` | `BT_ENTITLE_API_KEY` | All read-only commands (`list`, `get`) and admin writes (revoke, delete) |
15
+ | **User-context** | `entitle.user_api_key` | `BT_ENTITLE_USER_API_KEY` | On-behalf-of-a-user writes — currently `bt entitle requests create` |
16
+
17
+ The org/admin token **cannot** create access requests because requests are
18
+ made *as* a person, not on behalf of the org. If only the admin token is
19
+ configured, `bt entitle requests create` exits early with a panel explaining
20
+ how to add a user token.
21
+
22
+ ```yaml
23
+ # ~/.bt-cli/config.yaml
24
+ profiles:
25
+ default:
26
+ entitle:
27
+ api_url: https://api.us.entitle.io
28
+ api_key: <ORG_TOKEN> # broad read access
29
+ user_api_key: <USER_TOKEN> # personal token, for `requests create`
30
+ ```
31
+
32
+ The user token is generated from `app.entitle.io` → user profile → API tokens
33
+ → **Create user token** (different page from the org token).
34
+
8
35
  ## IMPORTANT: Destructive Operations
9
36
 
10
37
  **ALWAYS confirm with the user before:**
@@ -296,10 +323,146 @@ bt entitle roles list --resource <resource_id>
296
323
  **Kubernetes:** EKS, AKS, GKE, Rancher
297
324
  **BeyondTrust:** Password Safe, PRA, Remote Support
298
325
 
326
+ ## Newer command families
327
+
328
+ Added in 0.5.0; the CLI now covers 75 of the API's 77 operations.
329
+
330
+ ```bash
331
+ # Grant/revoke work items behind an access request (read + status only)
332
+ bt entitle tasks list
333
+ bt entitle tasks get <task-id>
334
+ bt entitle tasks set-status <task-id> --status todo|inProgress|done
335
+ bt entitle tasks archive <task-id>
336
+
337
+ # Agent tokens — these are real credentials
338
+ bt entitle agent-tokens list
339
+ bt entitle agent-tokens create --name "collector" -o json # token shown ONCE
340
+ bt entitle agent-tokens update <id> --name "new name"
341
+ bt entitle agent-tokens delete <id>
342
+
343
+ # Outbound webhooks — two separate families
344
+ bt entitle audit-webhooks list | get | create | update | enable | delete
345
+ bt entitle workflow-webhooks list | get | create | update | delete
346
+
347
+ # Delegation: send someone's queue to someone else
348
+ bt entitle request-forwards list | get | for-user <user-id> | create | delete
349
+ bt entitle review-forwards list | get | create | delete
350
+
351
+ # Identity plumbing
352
+ bt entitle directory-groups list --search "DYN-ROLE"
353
+ bt entitle user-accounts list | create | delete
354
+ bt entitle users pats <user-id>
355
+ bt entitle permissions account <account-id>
356
+ bt entitle chats channels --integration-type slack
357
+ ```
358
+
359
+ Writes were added to the families that previously had reads only: `roles`,
360
+ `policies`, `workflows`, `integrations` all gained create/update/delete, and
361
+ `resources` and `bundles` gained `update`.
362
+
363
+ ## Update semantics: PUT merges, but lists inside it do not
364
+
365
+ Live-verified per family. **Every family merges** — a field you leave out of the
366
+ body keeps its current value, so no command does read-modify-write and a `curl`
367
+ sending one field is safe.
368
+
369
+ Two qualifications that matter more than the rule:
370
+
371
+ - **A list you *do* send replaces that list wholesale.** `--role`, `--tag`,
372
+ `--group`, `--duration` set the whole collection; they never append. Passing
373
+ `-g` alone drops every other target.
374
+ - **`workflows` merges at the top level but REPLACES its `rules` array.** A
375
+ two-rule workflow plus a PUT carrying one rule leaves **one rule**, and each
376
+ rule object must be complete (`400 "rules.0.inGroups must be an array"`).
377
+
378
+ ## Writes that return 200 and do nothing
379
+
380
+ The most dangerous behaviour in this API. All live-verified — **verify a write
381
+ with a follow-up GET**, because the response will not tell you:
382
+
383
+ | Operation | Silently ignored |
384
+ |---|---|
385
+ | `POST /roles` | drops `prerequisitePermissions` (CLI issues a follow-up PUT) |
386
+ | `PUT /roles/{id}` | ignores `name`, `resource`, `virtualizedRole` |
387
+ | `PUT /integrations/{id}` | ignores `allowChangingAccountPermissions`, `allowCreatingAccounts`, `readonly` |
388
+ | `PUT /policies/{id}` | a `sortOrder` past the last policy keeps the old value |
389
+ | workflow webhooks | `approvalAlgorithmIds` is read-only |
390
+ | both webhook families | `{"enabled": false}` on create *and* update |
391
+ | integrations update | an unknown `--owner` (but create 404s on it) |
392
+
393
+ **`POST` on both forwards families is an UPSERT keyed on the forwarder** — a
394
+ second create for the same person returns the *same id* with the target
395
+ replaced, 200, no 409. "Create a forward for Alice" therefore silently
396
+ redirects her queue away from whoever it pointed at. Check with
397
+ `request-forwards for-user <id>` first.
398
+
399
+ **`POST /usersAccounts` is idempotent** on `(user, account)`: it returns the
400
+ pre-existing row with its original `createdAt` rather than creating a duplicate.
401
+ A script that deletes "the row it just created" will be deleting a real one.
402
+
403
+ ## Pagination: the envelope tells you
404
+
405
+ `pagination` present in the response means the endpoint really pages. Absent
406
+ means `page`/`perPage` are **silently ignored** and one GET returns everything —
407
+ requesting page 2 hands back the same first row.
408
+
409
+ | Ignores paging | Really pages |
410
+ |---|---|
411
+ | `/tasks`, `/applications`, `/auditLogsWebhooks`, `/workflowsWebhooks` | `/agentTokens`, `/directoryGroups`, `/usersAccounts`, both `*Forwards` |
412
+
413
+ `perPage` caps at **100** and 400s above it. `--limit` means *maximum results*
414
+ and bounds the fetch, so a small limit is cheap; it does not mean page size.
415
+
416
+ ## Secrets and redaction
417
+
418
+ - **`agent-tokens create` returns a ~776-character token exactly once.** An
419
+ immediate GET returns only `id` and `name`. It is surfaced solely under
420
+ `-o json`, redacted in tables, with a stderr warning that it cannot be
421
+ retrieved again. Capture it at creation or delete and recreate.
422
+ - **Webhook header values are redacted by header NAME, not per hook.**
423
+ `Authorization`, `X-Api-Key`, `api-key`, `X-Secret`, `Token` read back as
424
+ `***`; `Cookie` and arbitrary names come back verbatim. **A GET-edit-PUT
425
+ round trip would store the literal `***` over a real secret**, which is why
426
+ neither webhook family offers `--file` and update sends only your flags.
427
+
428
+ ## Tenant-shaped gotchas (not CLI bugs)
429
+
430
+ - **A policy has no name or description** — the body is only
431
+ `inGroups`/`bundles`/`roles`/`sortOrder`, so policies are trackable by UUID
432
+ alone. `POST` also rejects an empty grant (`"Policy must grant at least 1
433
+ permission"`), so a policy can never be created inert: any create is a real
434
+ grant for as long as it lives.
435
+ - **Webhooks cannot be created disabled** in either family, and `/disable`
436
+ 404s. `DELETE` is the only off switch — which is why `audit-webhooks` has
437
+ `enable` and deliberately no `--disable` flag.
438
+ - **Deleted webhook names stay reserved**: recreating a just-deleted name 400s
439
+ with "Resource already exists".
440
+ - **`chats channels`** needs the *user* token (the org token 401s) and accepts
441
+ only `--integration-type slack`. It returns
442
+ `500 chatsService.missingScopes` until the tenant's Slack app is
443
+ re-authorized with `channels:read,groups:read,mpim:read,im:read`; the CLI
444
+ exits 3 with that message rather than a generic failure.
445
+ - **`orgRoles` is unreachable** — `GET /orgRoles` and
446
+ `PUT /orgRoles/identities` are 401 on *both* tokens, the same org-level
447
+ gating Pathfinder has. These are the only two API operations the CLI does not
448
+ implement, and they stay unimplemented until the gateway opens up.
449
+ - **Bundle create needs at least one role** (`"roles must contain at least 1
450
+ elements"`), and **role create** needs only `name` + `resource` +
451
+ `requestable` — the spec's required `prerequisitePermissions` is the field
452
+ the server discards. Virtual Application resources also need
453
+ `virtualizedRole`.
454
+ - **Omitting either side of a `usersAccounts` body is a 500, not a 400.**
455
+
299
456
  ## API Notes
300
457
 
301
458
  - Base path: `/public/v1`
302
- - Pagination: `page`/`perPage`
303
- - Response: `{"result": [...], "pagination": {...}}`
459
+ - Pagination: `page`/`perPage`, but only where the response carries a
460
+ `pagination` key — see the table above. `perPage` caps at 100.
461
+ - Response: `{"result": [...]}`, with `pagination` alongside it on the
462
+ endpoints that really page. Single GETs wrap the object in `result` too;
463
+ `eligibleJitAccess` is the exception, returning `{roles, bundles, pagination}`.
304
464
  - All IDs are UUIDs (strings)
305
- - Bearer token auth
465
+ - Bearer token auth; some routes need the *user* token, not the org one
466
+ - Coverage: 75 of 77 operations. The two missing are the `orgRoles` pair,
467
+ blocked by org-level gating. `tests/entitle/test_coverage.py` fails if that
468
+ drifts.
@@ -0,0 +1,281 @@
1
+ ---
2
+ name: pf
3
+ description: Pathfinder platform commands — whoami, site/product access, PAT/MCP token listing, machine registry, user invites and product/site access grants, workload identities (OIDC trust for CI/pods/apps) and Cedar IAM policies that grant access to Workload Credentials. Use when working with the Pathfinder portal API (Nomine), platform PATs, site IDs, user onboarding, machine identities, workload identities, or IAM/RBAC policies.
4
+ ---
5
+
6
+ # Pathfinder Platform (`bt pf`)
7
+
8
+ The Pathfinder platform API (internal name: **Nomine**) manages the BeyondTrust
9
+ cloud portal layer: users, sites, product access, tokens, and machine
10
+ identities, plus the auth service's **workload identities** and the **IAM
11
+ policy service** (Cedar RBAC). `bt pf` talks to it through the public API
12
+ gateway with PAT auth — the same pattern as `bt wlc` / `bt epml`.
13
+
14
+ ## Configuration
15
+
16
+ ```bash
17
+ export BT_PF_API_URL=https://api.beyondtrust.io # default
18
+ export BT_PF_SITE_ID=<site-uuid> # required — the site the PAT was minted on
19
+ export BT_PF_PAT=PAT_xxx # required (mint at app.beyondtrust.io → Manage Profile)
20
+ ```
21
+
22
+ Site ID discovery: log into app.beyondtrust.io, choose your site, then browse
23
+ to `https://app.beyondtrust.io/api/platform/currentSite`.
24
+
25
+ **For `bt pf policies` and `bt pf wi`, point `pf` at the Administration site**:
26
+ the **org id doubles as the Administration site id**, and the PAT must be
27
+ minted there. The policy commands also need the **WLC site id** for Cedar's
28
+ `@siteId` — taken from the `wlc` config (`BT_WLC_SITE_ID`), overridable with
29
+ `--site`. Two sites, two PATs, because PATs are site-bound.
30
+
31
+ ## URL mapping (the thing everyone gets wrong)
32
+
33
+ The Nomine swagger writes paths as `/api/auth/X`. On the gateway the product
34
+ segment is `platform` and the `auth/` prefix stays:
35
+
36
+ spec /api/auth/UserInfo → https://api.beyondtrust.io/site/<site-id>/platform/auth/UserInfo
37
+
38
+ **PATs are site-bound**: minted while a site is active in the portal, valid
39
+ only for that site's `/site/<id>/...` paths. Any other site → `401 Access
40
+ denied for this site`. A just-minted PAT can take ~1s to propagate
41
+ (transient `401 Personal access token not found` — retry once).
42
+
43
+ ## Commands
44
+
45
+ ```bash
46
+ bt pf auth test # connectivity + PAT validity
47
+ bt pf whoami # UserInfo for the PAT owner
48
+ bt pf access # site + product access rules (also -o json)
49
+ bt pf pats list # PATs (values masked; --show-tokens to reveal)
50
+ bt pf mcp-tokens list # MCP tokens (same masking)
51
+ bt pf machines list # registered machine IDs
52
+ bt pf machines get <uuid> # one machine's record (secret hash never shown)
53
+ bt pf wi list # workload identities (alias of `bt pf workload-identities`)
54
+ bt pf policies list # Cedar IAM policies (deleted ones hidden; --all shows them)
55
+ ```
56
+
57
+ ## User onboarding & access assignment (`bt pf users`)
58
+
59
+ ```bash
60
+ # Invite a user to the org (sends a real email; confirms unless --yes).
61
+ # Default site = BT_PF_SITE_ID; -p grants products on that site at invite time.
62
+ bt pf users invite -e new.user@corp.com --first-name New --last-name User \
63
+ -p pra -p epml
64
+
65
+ bt pf users cancel-invite new.user@corp.com # cancel a pending invite
66
+
67
+ # Per-user product access on a site (product slugs are lowercase:
68
+ # epmwm, pra, psc, rs, entitle, epml, insights, secrets)
69
+ bt pf users grant -u <user-uuid> -p epml # default site
70
+ bt pf users revoke -u <user-uuid> -p epml -s <site-uuid>
71
+
72
+ # Site membership (optionally granting products in the same call)
73
+ bt pf users add-to-site -u <user-uuid> -s <site-uuid> -p pra -p psc
74
+ bt pf users remove-from-site -u <user-uuid> -s <site-uuid>
75
+
76
+ bt pf users delete <user-uuid> [...] # bulk delete (org admin; confirms)
77
+ bt pf users list # every page (admin-site PAT with 'admin' scope); --page N for one
78
+ ```
79
+
80
+ **PAT matters (re-verified 2026-10-21):** the org-admin surface answers only a
81
+ PAT minted on the **Administration site** (`BT_PF_SITE_ID` = the org id) that
82
+ carries the **`admin`** scope. With that PAT, `auth/Users`, `auth/SAMLProviders`,
83
+ `auth/Auditing` and `auth/{productaccess,siteaccess,access}/org` are all **200**.
84
+ A product-site PAT still gets the empty-body 403 on every one of them, and that
85
+ is what every PAT we had on 2026-06-11 got, which is why these commands looked
86
+ permanently blocked. Every Administration-site command (`users`, `policies`, `wi`)
87
+ shares one contract: an empty-body 403 (the wrong kind of PAT) exits **3** with a
88
+ hint naming the fix; a 401, or a 403 with a body, exits 1.
89
+
90
+ Writes work too, with the admin-scoped PAT (2026-10-21):
91
+ - `bt pf users delete` removes the Pathfinder user record only, never the IdP
92
+ (Entra ID etc.) account.
93
+ - `bt pf users invite -s <site> -p <slug>...` → `{inviteId, userId,
94
+ organizationId, email, isFederated}`. The user then lists as `status:
95
+ Invited`, `active: false`, with a **new** user id. A re-invited former SAML
96
+ user came back with `isFederated: false`.
97
+
98
+ - `bt pf users add-to-site` / `remove-from-site` / `grant` / `revoke` all work
99
+ (live round trip on a test user, 2026-10-21, net zero). Order matters:
100
+ - **Site first:** a product grant on a site the user isn't a member of →
101
+ `404 "User does not have site access"`. Run `add-to-site` first.
102
+ - **Revoking a rule that doesn't exist** → `500 "Unable to remove product
103
+ access rule."` (exit 1), not a 404.
104
+ - **Reading access back:** site membership is visible through `users list
105
+ --site-id <site>` (`auth/Users?siteId=`). Product rules aren't:
106
+ `auth/productaccess?userId=` is in the swagger but 404s on the gateway.
107
+ - **A pending invite creates no site rule** until it's accepted, so an invitee
108
+ doesn't show in `users list --site-id` for the invited site.
109
+ - `cancel-invite` removes the invitation, but the user record keeps
110
+ `status: Invited`, so the list can't tell a live invite from a cancelled one.
111
+ Re-inviting the same email **reuses that user id** and sends a fresh email.
112
+ After a full `delete`, the next invite gets a new id.
113
+ - The org's *default* access rules (`auth/{site,product}access/org`) may grant
114
+ products to every user anyway. `invite` sends a
115
+ real email. Note `invite`'s default site is `BT_PF_SITE_ID`, which for these
116
+ commands is the Administration site, so pass `-s <product-site>` to grant
117
+ products on a real site.
118
+
119
+ `users list` pages for you: without paging params the server returns a bare
120
+ array **capped at 200** (no total), and with `page`/`pageSize` it returns
121
+ `{users, totalCount}`. The CLI asks for 1000 per page, walks pages to
122
+ `totalCount`, de-duplicates by id, and warns if it comes up short.
123
+ `--page N` fetches just that page.
124
+
125
+ ## Workload identities (`bt pf workload-identities`, alias `bt pf wi`)
126
+
127
+ OIDC issuer trust configs for non-human callers (CI jobs, k8s pods, Azure
128
+ apps). Administration site, admin PAT. An identity is granted **nothing**
129
+ until a policy names it.
130
+
131
+ | Command | Method & path |
132
+ |---|---|
133
+ | `list [--wlc] [--for-site S]` | `GET /site/{admin}/platform/auth/workload-identities` → `{issuers: [...], totalCount}` |
134
+ | `get <id>` | `GET .../workload-identities/{id}` → `{issuer: {...}}` — CLI also lists the live policies naming it |
135
+ | `create` | `POST .../workload-identities` → `{issuer}` |
136
+ | `update <id>` | `PUT .../workload-identities/{id}` — full body, read-modify-write |
137
+ | `delete <id>` | `DELETE .../workload-identities/{id}` (confirms; `--yes`) |
138
+
139
+ Item keys (live): `conditions, description, expectedAud, identityId,
140
+ idpCategory, issuerUrl, organizationId, registeredScopes, scopeLevel,
141
+ serviceName, siteId, sites` (`sites[]` = `{siteId, registeredScopes,
142
+ scopeLevel, expectedAud}`).
143
+
144
+ ```bash
145
+ bt pf wi create -n k8s-wc-reader --issuer-url https://kubernetes.default.svc --idp-category Custom \
146
+ -c sub=system:serviceaccount:demo-app:wc-reader --scope secrets --dry-run
147
+ bt pf wi create -n gh-deploy --issuer-url https://token.actions.githubusercontent.com \
148
+ --idp-category GitHubActions -c "sub=repo:owner/repo:*" -c ref=refs/heads/main
149
+ bt pf wi update <id> --description "new" # -c / --conditions-file REPLACE all conditions
150
+ ```
151
+
152
+ Rules checked client-side (ported from the Terraform provider):
153
+ `idpCategory` ∈ GitHubActions | AzureEntra | Custom; ≥ 1 registered scope
154
+ (`secrets` = WLC, default; `psc`, `admin` also seen); condition values
155
+ non-empty, ≤ 256 chars, at most one `*` and only at the end; GitHubActions
156
+ keys ∈ {sub, repository, repository_owner, ref, job_workflow_ref,
157
+ workflow_ref, environment, event_name, actor} and `sub` must start
158
+ `repo:<owner>/<repo>`. Only `conditions`, `description`, `scopeLevel`,
159
+ `registeredScopes` are updatable; the service lower-cases `serviceName` and
160
+ `issuerUrl`.
161
+
162
+ ## IAM policies (`bt pf policies`) — Cedar RBAC for Workload Credentials
163
+
164
+ | Command | Method & path (admin site) |
165
+ |---|---|
166
+ | `list [--all]` | `GET /site/{admin}/platform/iam/policies` → `{policies: [...]}`; DELETED / PENDING_UNBIND hidden unless `--all` |
167
+ | `get <name>` | `GET .../iam/policies/{name}` |
168
+ | `apply <name> -f p.cedar` / `--stdin` | `PUT .../iam/policies/{name}`, **`Content-Type: text/plain`**, raw Cedar — upsert |
169
+ | `grant ...` | build Cedar from flags, show it, confirm (says REPLACE if the name is live), PUT, wait |
170
+ | `render ...` | same flags, print Cedar (`--curl` for the curl). No write |
171
+ | `validate -f p.cedar` | local checks only |
172
+ | `delete <name>` | `DELETE .../iam/policies/{name}` — soft delete (then listed as DELETED) |
173
+ | `wait <name>` | poll until ACTIVE / WAITING_FOR_RESOURCE (`--timeout`) |
174
+
175
+ Policy object: `name, cedar, status, product, site, createdAt, updatedAt`
176
+ (no id, no description). No pagination (query params ignored, 2026-10-21).
177
+
178
+ Cedar the service accepts — one `permit`, `@siteId` = the site the grant
179
+ applies to (the **WLC** site, not the admin site):
180
+
181
+ ```cedar
182
+ @siteId("<wlc-site-uuid>")
183
+ permit(
184
+ principal == Pathfinder::Workload::Id::"<identityId>", // or User::Email / Group / Role
185
+ action in [WorkloadCredentials::Action::"ReadSecret", WorkloadCredentials::Action::"ListFolderContents"],
186
+ resource == WorkloadCredentials::Folder::"/btlab/app/demo" // or Secret / DynamicSecret / Integration, or `resource is WorkloadCredentials::Product`
187
+ );
188
+ ```
189
+
190
+ Not supported: `forbid`, `when`, `unless`, multiple statements, missing
191
+ `@siteId`. Roles are written as actions (`WorkloadCredentials::Action::"Viewer"`,
192
+ `Pathfinder::Action::"Admin"`). Full action catalog, role expansion and the
193
+ folder cascade are in the `/wlc` skill.
194
+
195
+ ```bash
196
+ # Viewer for k8s-wc-reader on a folder (cascades to everything below)
197
+ bt pf policies grant --name k8s-wc-reader-demo-viewer --workload-name k8s-wc-reader \
198
+ --folder /btlab/app/demo --role-grant Viewer
199
+ # ReadSecret on one secret
200
+ bt pf policies grant --name k8s-db-pass --workload <id> --secret /btlab/app/demo/db-pass \
201
+ --action ReadSecret --action ReadSecretMetadata
202
+ # Revoke
203
+ bt pf policies delete k8s-db-pass
204
+ ```
205
+
206
+ Gotchas (live-verified 2026-10-20/21):
207
+ - The **docs' path `/platform/iam/policies` is wrong** — it is
208
+ `/site/{admin-site}/platform/iam/policies`.
209
+ - The list **includes soft-deleted** policies — history, never grants.
210
+ - **PUT is an upsert keyed by name** (unique per org). Reusing a name silently
211
+ replaces it; `grant`/`apply` say REPLACE and show the current Cedar. A
212
+ DELETED name is reusable.
213
+ - **A wrong workload id still goes ACTIVE** (the service doesn't validate
214
+ principals) and grants nothing — `grant` warns.
215
+ - **A missing path → WAITING_FOR_RESOURCE**, which binds when it's created.
216
+ - Statuses: `RESOURCE_LOOKUP`, `PENDING_BIND` (settling) → `ACTIVE`;
217
+ `WAITING_FOR_RESOURCE`; `RESOURCE_LOOKUP_ERROR`, `NO_PRODUCT_ACCESS`
218
+ (failed); `PENDING_UNBIND` → `DELETED`.
219
+ - Names: 1-100 chars, letters/digits/`-`/`_`, alnum at both ends (the
220
+ provider's server-mirrored rule — no dots).
221
+ - A non-admin-site PAT on the policy API → `403 forbidden: caller cannot
222
+ perform this action`.
223
+
224
+ ## Groups and roles — known gap
225
+
226
+ Policies can name `Pathfinder::Group::"name"` and `Pathfinder::Role::"name"`,
227
+ but **group and role membership is not visible to PAT callers, so access
228
+ through groups can't be resolved.** There is no groups/roles API in the Nomine
229
+ swagger or the Terraform provider, and read-only probes on the Administration
230
+ site (2026-10-21) found none: `platform/auth/{groups,Groups,roles,Roles,UserGroups}`
231
+ and `platform/iam/{groups,Groups,roles}` → 404; `platform/auth/User/Groups` →
232
+ 405 (a route exists but not for GET; not probed further). The only membership
233
+ data a PAT sees is the caller's own `groups` in `bt pf whoami`. `bt wlc access`
234
+ reports group/role grants as "membership not resolved", and always notes that
235
+ Admin-console role assignments can make effective access larger than policies show.
236
+
237
+ ## What works over PAT vs what doesn't (re-verified 2026-10-21)
238
+
239
+ | Endpoint | Product-site PAT | Administration-site PAT (`admin` scope) |
240
+ |----------|------------------|------------------------------------------|
241
+ | `auth/UserInfo`, `auth/User/Access`, `auth/User/Token`, `auth/User/McpToken` | 200 | 200 |
242
+ | `auth/Users`, `auth/SAMLProviders`, `auth/Auditing` | 403 (empty body) | **200** |
243
+ | `auth/productaccess/org`, `auth/access/org`, `auth/siteaccess/org` | 403 (empty body) | **200** |
244
+ | `auth/workload-identities`, `iam/policies` | 403 | **200** |
245
+ | `auth/Machine`, `auth/Machine/{id}` | **200** | 403 |
246
+ | `auth/productaccess` (collection GET) | 404 (not deployed) | n/a |
247
+ | `auth/Users/delete` | 405 on GET; POST → 403 (2026-06-11) | **POST works** (deleted a test user, 2026-10-21) |
248
+ | `auth/UserInvitation` | 405 on GET; POST → 403 (2026-06-11) | **POST works**: invited a user to a product site with products, 2026-10-21 |
249
+ | `auth/UserInvitation/Cancel` | 405 on GET | **POST works** (2026-10-21); a second cancel → `404 "User invitation not found."` |
250
+ | `auth/UserInvitation/Product` | 405 on GET | 405 on GET; POST not yet tested |
251
+ | `auth/siteaccess/{s}/user/{u}` | 405 on GET | **POST and DELETE work** (2026-10-21) |
252
+ | `auth/productaccess/{p}/site/{s}/user/{u}` | 405 on GET; grant POST → empty-body 403 (2026-06-11) | **POST and DELETE work** (2026-10-21) |
253
+ | `auth/productaccess?userId=` (read a user's product rules, in the swagger) | 404 | 404: not deployed, so a user's product grants can't be read back |
254
+
255
+ PAT `access` scopes are listed in `bt pf pats list`. The Administration-site PAT
256
+ in use on 2026-10-21 carried `admin` plus every product slug. Note that machines
257
+ work the other way round: they need a product-site PAT.
258
+
259
+ ## Machines
260
+
261
+ Platform machine identities: non-user principals (machineId + machineSecret)
262
+ used by agents/integrations to authenticate. `POST
263
+ login.beyondtrust.io/bt.identity/Machine/AccessToken {machineId,
264
+ machineSecret}` → 30-min JWT (machineEndpoints.* scopes only). Records carry
265
+ an activation-token lifecycle (created/expiration) and `active` flag —
266
+ an entry with `active: false` and a pending activation token was registered
267
+ but never activated.
268
+
269
+ ## Not exposed via PAT (cookie-auth only, app.beyondtrust.io/api/auth/*)
270
+
271
+ User listing, auditing, SAML providers and the org-default access-rule reads
272
+ now work over an Administration-site PAT with the `admin` scope (see above).
273
+ Still cookie-only as far as tested: PAT/MCP token creation and revocation, and
274
+ invitation Accept. These need the `access` session cookie
275
+ (browser JWT ~5min TTL, or the programmatic SignIn → /session/login flow —
276
+ password-auth users only, not federated).
277
+
278
+ Invite body shape (spec `InviteOrganizationUserInput`): `{email, firstName,
279
+ lastName, siteMetaData: [{siteId, name, residency}], productMetaData:
280
+ [{name, siteId, siteName}]}` — `bt pf users invite` builds this, defaulting
281
+ the site to `BT_PF_SITE_ID` and omitting name/residency unless given.
@@ -14,6 +14,9 @@ description: Privileged Remote Access commands for jump items, vault accounts, a
14
14
  - `bt pra jump-items tunnel delete` - Deletes protocol tunnel
15
15
  - `bt pra jump-groups delete` - Deletes jump group
16
16
  - `bt pra vault accounts delete` - Deletes vault account
17
+ - `bt pra users delete` - Deletes a user (non-admins only)
18
+ - `bt pra policies group delete` - Deletes a Group Policy
19
+ - `bt pra policies group {jumpoints|members|jump-groups|vault-accounts|vault-account-groups|teams} remove` - Detaches a sub-resource from a Group Policy
17
20
 
18
21
  List affected resources first, then ask for explicit confirmation.
19
22
 
@@ -0,0 +1,19 @@
1
+ ---
2
+ name: secrets
3
+ description: Old name for Workload Credentials (WLC). `bt secrets` is a deprecated alias of `bt wlc` — use the /wlc skill.
4
+ ---
5
+
6
+ # Secrets API → Workload Credentials (`bt wlc`)
7
+
8
+ BeyondTrust renamed the Secrets API (Secrets Safe) to **Workload Credentials
9
+ (WLC)**. In bt-cli 0.6.0 the commands moved to **`bt wlc`**; `bt secrets` is a
10
+ deprecated alias with the same commands (it prints a one-line notice on
11
+ stderr, so `-o json` output is unchanged).
12
+
13
+ - Commands: `bt wlc folders|static|dynamic|leases|integrations|auth|tree|access`
14
+ - Config: `BT_WLC_*` (falls back to `BT_SECRETS_*`), `wlc` profile section (falls back to `secrets`)
15
+ - URL prefix: `/site/{id}/wlc` (`/site/{id}/secrets` still answers)
16
+ - Access policies & workload identities: `bt pf policies`, `bt pf wi`
17
+
18
+ **Use the `/wlc` skill** — it has everything that used to be here, plus the
19
+ Cedar policy and effective-access material.