bt-cli 0.4.68__tar.gz → 0.4.69__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 (249) hide show
  1. {bt_cli-0.4.68 → bt_cli-0.4.69}/CLAUDE.md +1 -1
  2. {bt_cli-0.4.68 → bt_cli-0.4.69}/PKG-INFO +1 -1
  3. {bt_cli-0.4.68 → bt_cli-0.4.69}/pyproject.toml +1 -1
  4. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/__init__.py +1 -1
  5. bt_cli-0.4.69/src/bt_cli/data/skills/session-analysis/SKILL.md +72 -0
  6. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/pws/commands/sessions.py +30 -1
  7. bt_cli-0.4.68/src/bt_cli/data/skills/session-analysis/SKILL.md +0 -64
  8. {bt_cli-0.4.68 → bt_cli-0.4.69}/.claude/skills/bt/SKILL.md +0 -0
  9. {bt_cli-0.4.68 → bt_cli-0.4.69}/.claude/skills/entitle/SKILL.md +0 -0
  10. {bt_cli-0.4.68 → bt_cli-0.4.69}/.claude/skills/epml/SKILL.md +0 -0
  11. {bt_cli-0.4.68 → bt_cli-0.4.69}/.claude/skills/epmw/SKILL.md +0 -0
  12. {bt_cli-0.4.68 → bt_cli-0.4.69}/.claude/skills/pra/SKILL.md +0 -0
  13. {bt_cli-0.4.68 → bt_cli-0.4.69}/.claude/skills/pws/SKILL.md +0 -0
  14. {bt_cli-0.4.68 → bt_cli-0.4.69}/.env.example +0 -0
  15. {bt_cli-0.4.68 → bt_cli-0.4.69}/.github/workflows/ci.yml +0 -0
  16. {bt_cli-0.4.68 → bt_cli-0.4.69}/.github/workflows/release.yml +0 -0
  17. {bt_cli-0.4.68 → bt_cli-0.4.69}/.gitignore +0 -0
  18. {bt_cli-0.4.68 → bt_cli-0.4.69}/README.md +0 -0
  19. {bt_cli-0.4.68 → bt_cli-0.4.69}/assets/cli-help.png +0 -0
  20. {bt_cli-0.4.68 → bt_cli-0.4.69}/assets/cli-output.png +0 -0
  21. {bt_cli-0.4.68 → bt_cli-0.4.69}/bt-cli.spec +0 -0
  22. {bt_cli-0.4.68 → bt_cli-0.4.69}/bt_entry.py +0 -0
  23. {bt_cli-0.4.68 → bt_cli-0.4.69}/epml-clients-server-side-filters-plan.md +0 -0
  24. {bt_cli-0.4.68 → bt_cli-0.4.69}/epml-implementation-plan.md +0 -0
  25. {bt_cli-0.4.68 → bt_cli-0.4.69}/pf-implementation-plan.md +0 -0
  26. {bt_cli-0.4.68 → bt_cli-0.4.69}/scripts/bt_entry.py +0 -0
  27. {bt_cli-0.4.68 → bt_cli-0.4.69}/scripts/pf_onboard.py +0 -0
  28. {bt_cli-0.4.68 → bt_cli-0.4.69}/scripts/sync-package-data.sh +0 -0
  29. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/cli.py +0 -0
  30. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/commands/__init__.py +0 -0
  31. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/commands/configure.py +0 -0
  32. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/commands/learn.py +0 -0
  33. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/commands/quick.py +0 -0
  34. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/core/__init__.py +0 -0
  35. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/core/auth.py +0 -0
  36. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/core/client.py +0 -0
  37. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/core/config.py +0 -0
  38. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/core/config_file.py +0 -0
  39. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/core/csv_utils.py +0 -0
  40. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/core/errors.py +0 -0
  41. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/core/output.py +0 -0
  42. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/core/prompts.py +0 -0
  43. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/core/rest_debug.py +0 -0
  44. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/data/CLAUDE.md +0 -0
  45. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/data/__init__.py +0 -0
  46. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/data/skills/bt/SKILL.md +0 -0
  47. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/data/skills/entitle/SKILL.md +0 -0
  48. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/data/skills/epml/SKILL.md +0 -0
  49. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/data/skills/epmw/SKILL.md +0 -0
  50. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/data/skills/pf/SKILL.md +0 -0
  51. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/data/skills/pra/SKILL.md +0 -0
  52. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/data/skills/pws/SKILL.md +0 -0
  53. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/data/skills/secrets/SKILL.md +0 -0
  54. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/entitle/__init__.py +0 -0
  55. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/entitle/client/__init__.py +0 -0
  56. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/entitle/client/base.py +0 -0
  57. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/entitle/commands/__init__.py +0 -0
  58. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/entitle/commands/accounts.py +0 -0
  59. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/entitle/commands/applications.py +0 -0
  60. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/entitle/commands/auth.py +0 -0
  61. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/entitle/commands/bundles.py +0 -0
  62. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/entitle/commands/integrations.py +0 -0
  63. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/entitle/commands/permissions.py +0 -0
  64. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/entitle/commands/policies.py +0 -0
  65. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/entitle/commands/requests.py +0 -0
  66. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/entitle/commands/resources.py +0 -0
  67. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/entitle/commands/roles.py +0 -0
  68. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/entitle/commands/users.py +0 -0
  69. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/entitle/commands/workflows.py +0 -0
  70. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/entitle/models/__init__.py +0 -0
  71. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/entitle/models/bundle.py +0 -0
  72. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/entitle/models/common.py +0 -0
  73. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/entitle/models/integration.py +0 -0
  74. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/entitle/models/permission.py +0 -0
  75. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/entitle/models/policy.py +0 -0
  76. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/entitle/models/resource.py +0 -0
  77. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/entitle/models/role.py +0 -0
  78. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/entitle/models/user.py +0 -0
  79. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/entitle/models/workflow.py +0 -0
  80. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/epml/__init__.py +0 -0
  81. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/epml/client/__init__.py +0 -0
  82. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/epml/client/base.py +0 -0
  83. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/epml/commands/__init__.py +0 -0
  84. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/epml/commands/audit.py +0 -0
  85. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/epml/commands/auth.py +0 -0
  86. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/epml/commands/client_pkg.py +0 -0
  87. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/epml/commands/clients.py +0 -0
  88. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/epml/commands/external_apis.py +0 -0
  89. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/epml/commands/hosts.py +0 -0
  90. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/epml/commands/iolog.py +0 -0
  91. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/epml/commands/license.py +0 -0
  92. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/epml/commands/quick.py +0 -0
  93. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/epml/commands/rbp_cmdgrps.py +0 -0
  94. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/epml/commands/rbp_entitlement.py +0 -0
  95. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/epml/commands/rbp_hostgrps.py +0 -0
  96. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/epml/commands/rbp_policy.py +0 -0
  97. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/epml/commands/rbp_roles.py +0 -0
  98. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/epml/commands/rbp_tests.py +0 -0
  99. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/epml/commands/rbp_tmdategrps.py +0 -0
  100. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/epml/commands/rbp_tx.py +0 -0
  101. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/epml/commands/rbp_usergrps.py +0 -0
  102. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/epml/commands/settings.py +0 -0
  103. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/epml/commands/siems.py +0 -0
  104. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/epml/commands/users.py +0 -0
  105. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/epml/models/__init__.py +0 -0
  106. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/epmw/__init__.py +0 -0
  107. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/epmw/client/__init__.py +0 -0
  108. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/epmw/client/base.py +0 -0
  109. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/epmw/commands/__init__.py +0 -0
  110. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/epmw/commands/audits.py +0 -0
  111. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/epmw/commands/auth.py +0 -0
  112. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/epmw/commands/computers.py +0 -0
  113. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/epmw/commands/events.py +0 -0
  114. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/epmw/commands/groups.py +0 -0
  115. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/epmw/commands/policies.py +0 -0
  116. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/epmw/commands/quick.py +0 -0
  117. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/epmw/commands/requests.py +0 -0
  118. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/epmw/commands/roles.py +0 -0
  119. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/epmw/commands/tasks.py +0 -0
  120. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/epmw/commands/users.py +0 -0
  121. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/epmw/models/__init__.py +0 -0
  122. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/pf/__init__.py +0 -0
  123. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/pf/client/__init__.py +0 -0
  124. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/pf/client/base.py +0 -0
  125. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/pf/commands/__init__.py +0 -0
  126. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/pf/commands/auth.py +0 -0
  127. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/pf/commands/machines.py +0 -0
  128. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/pf/commands/tokens.py +0 -0
  129. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/pf/commands/user.py +0 -0
  130. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/pf/commands/users.py +0 -0
  131. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/pra/__init__.py +0 -0
  132. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/pra/client/__init__.py +0 -0
  133. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/pra/client/base.py +0 -0
  134. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/pra/commands/__init__.py +0 -0
  135. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/pra/commands/auth.py +0 -0
  136. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/pra/commands/group_policies.py +0 -0
  137. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/pra/commands/import_export.py +0 -0
  138. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/pra/commands/jump_clients.py +0 -0
  139. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/pra/commands/jump_groups.py +0 -0
  140. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/pra/commands/jump_items.py +0 -0
  141. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/pra/commands/jumpoints.py +0 -0
  142. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/pra/commands/policies.py +0 -0
  143. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/pra/commands/quick.py +0 -0
  144. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/pra/commands/teams.py +0 -0
  145. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/pra/commands/users.py +0 -0
  146. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/pra/commands/vault.py +0 -0
  147. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/pra/models/__init__.py +0 -0
  148. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/pra/models/common.py +0 -0
  149. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/pra/models/group_policy.py +0 -0
  150. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/pra/models/jump_client.py +0 -0
  151. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/pra/models/jump_group.py +0 -0
  152. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/pra/models/jump_item.py +0 -0
  153. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/pra/models/jumpoint.py +0 -0
  154. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/pra/models/team.py +0 -0
  155. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/pra/models/user.py +0 -0
  156. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/pra/models/vault.py +0 -0
  157. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/pws/__init__.py +0 -0
  158. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/pws/client/__init__.py +0 -0
  159. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/pws/client/base.py +0 -0
  160. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/pws/client/beyondinsight.py +0 -0
  161. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/pws/client/passwordsafe.py +0 -0
  162. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/pws/commands/__init__.py +0 -0
  163. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/pws/commands/accounts.py +0 -0
  164. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/pws/commands/assets.py +0 -0
  165. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/pws/commands/attributes.py +0 -0
  166. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/pws/commands/audit.py +0 -0
  167. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/pws/commands/auth.py +0 -0
  168. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/pws/commands/clouds.py +0 -0
  169. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/pws/commands/config.py +0 -0
  170. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/pws/commands/credentials.py +0 -0
  171. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/pws/commands/databases.py +0 -0
  172. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/pws/commands/directories.py +0 -0
  173. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/pws/commands/functional.py +0 -0
  174. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/pws/commands/import_export.py +0 -0
  175. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/pws/commands/platforms.py +0 -0
  176. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/pws/commands/quick.py +0 -0
  177. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/pws/commands/search.py +0 -0
  178. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/pws/commands/secrets.py +0 -0
  179. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/pws/commands/systems.py +0 -0
  180. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/pws/commands/users.py +0 -0
  181. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/pws/commands/workgroups.py +0 -0
  182. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/pws/config.py +0 -0
  183. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/pws/models/__init__.py +0 -0
  184. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/pws/models/account.py +0 -0
  185. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/pws/models/asset.py +0 -0
  186. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/pws/models/common.py +0 -0
  187. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/pws/models/system.py +0 -0
  188. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/secrets/__init__.py +0 -0
  189. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/secrets/client/__init__.py +0 -0
  190. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/secrets/client/base.py +0 -0
  191. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/secrets/commands/__init__.py +0 -0
  192. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/secrets/commands/_hints.py +0 -0
  193. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/secrets/commands/auth.py +0 -0
  194. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/secrets/commands/dynamic.py +0 -0
  195. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/secrets/commands/folders.py +0 -0
  196. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/secrets/commands/integrations.py +0 -0
  197. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/secrets/commands/leases.py +0 -0
  198. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/secrets/commands/static.py +0 -0
  199. {bt_cli-0.4.68 → bt_cli-0.4.69}/src/bt_cli/secrets/models/__init__.py +0 -0
  200. {bt_cli-0.4.68 → bt_cli-0.4.69}/tests/__init__.py +0 -0
  201. {bt_cli-0.4.68 → bt_cli-0.4.69}/tests/conftest.py +0 -0
  202. {bt_cli-0.4.68 → bt_cli-0.4.69}/tests/core/__init__.py +0 -0
  203. {bt_cli-0.4.68 → bt_cli-0.4.69}/tests/core/test_auth.py +0 -0
  204. {bt_cli-0.4.68 → bt_cli-0.4.69}/tests/core/test_config.py +0 -0
  205. {bt_cli-0.4.68 → bt_cli-0.4.69}/tests/core/test_config_file.py +0 -0
  206. {bt_cli-0.4.68 → bt_cli-0.4.69}/tests/core/test_errors.py +0 -0
  207. {bt_cli-0.4.68 → bt_cli-0.4.69}/tests/core/test_output.py +0 -0
  208. {bt_cli-0.4.68 → bt_cli-0.4.69}/tests/core/test_rest_debug.py +0 -0
  209. {bt_cli-0.4.68 → bt_cli-0.4.69}/tests/entitle/__init__.py +0 -0
  210. {bt_cli-0.4.68 → bt_cli-0.4.69}/tests/entitle/test_client.py +0 -0
  211. {bt_cli-0.4.68 → bt_cli-0.4.69}/tests/entitle/test_commands.py +0 -0
  212. {bt_cli-0.4.68 → bt_cli-0.4.69}/tests/entitle-smoke-test.sh +0 -0
  213. {bt_cli-0.4.68 → bt_cli-0.4.69}/tests/epml/__init__.py +0 -0
  214. {bt_cli-0.4.68 → bt_cli-0.4.69}/tests/epml/test_client.py +0 -0
  215. {bt_cli-0.4.68 → bt_cli-0.4.69}/tests/epml/test_commands.py +0 -0
  216. {bt_cli-0.4.68 → bt_cli-0.4.69}/tests/epmw/__init__.py +0 -0
  217. {bt_cli-0.4.68 → bt_cli-0.4.69}/tests/epmw/test_client.py +0 -0
  218. {bt_cli-0.4.68 → bt_cli-0.4.69}/tests/epmw/test_commands.py +0 -0
  219. {bt_cli-0.4.68 → bt_cli-0.4.69}/tests/epmw-quick-test-plan.md +0 -0
  220. {bt_cli-0.4.68 → bt_cli-0.4.69}/tests/fixtures/__init__.py +0 -0
  221. {bt_cli-0.4.68 → bt_cli-0.4.69}/tests/fixtures/responses.py +0 -0
  222. {bt_cli-0.4.68 → bt_cli-0.4.69}/tests/integration/__init__.py +0 -0
  223. {bt_cli-0.4.68 → bt_cli-0.4.69}/tests/integration/conftest.py +0 -0
  224. {bt_cli-0.4.68 → bt_cli-0.4.69}/tests/integration/helpers.py +0 -0
  225. {bt_cli-0.4.68 → bt_cli-0.4.69}/tests/integration/test_entitle_integration.py +0 -0
  226. {bt_cli-0.4.68 → bt_cli-0.4.69}/tests/integration/test_epmw_integration.py +0 -0
  227. {bt_cli-0.4.68 → bt_cli-0.4.69}/tests/integration/test_epmw_lifecycle.py +0 -0
  228. {bt_cli-0.4.68 → bt_cli-0.4.69}/tests/integration/test_pra_integration.py +0 -0
  229. {bt_cli-0.4.68 → bt_cli-0.4.69}/tests/integration/test_pra_lifecycle.py +0 -0
  230. {bt_cli-0.4.68 → bt_cli-0.4.69}/tests/integration/test_pws_integration.py +0 -0
  231. {bt_cli-0.4.68 → bt_cli-0.4.69}/tests/integration/test_pws_lifecycle.py +0 -0
  232. {bt_cli-0.4.68 → bt_cli-0.4.69}/tests/pf/__init__.py +0 -0
  233. {bt_cli-0.4.68 → bt_cli-0.4.69}/tests/pf/test_client.py +0 -0
  234. {bt_cli-0.4.68 → bt_cli-0.4.69}/tests/pf/test_commands.py +0 -0
  235. {bt_cli-0.4.68 → bt_cli-0.4.69}/tests/pra/__init__.py +0 -0
  236. {bt_cli-0.4.68 → bt_cli-0.4.69}/tests/pra/test_client.py +0 -0
  237. {bt_cli-0.4.68 → bt_cli-0.4.69}/tests/pra/test_commands.py +0 -0
  238. {bt_cli-0.4.68 → bt_cli-0.4.69}/tests/pra/test_jump_item_fixes.py +0 -0
  239. {bt_cli-0.4.68 → bt_cli-0.4.69}/tests/pra-smoke-test.sh +0 -0
  240. {bt_cli-0.4.68 → bt_cli-0.4.69}/tests/pra-test-plan.md +0 -0
  241. {bt_cli-0.4.68 → bt_cli-0.4.69}/tests/pws/__init__.py +0 -0
  242. {bt_cli-0.4.68 → bt_cli-0.4.69}/tests/pws/test_client.py +0 -0
  243. {bt_cli-0.4.68 → bt_cli-0.4.69}/tests/pws/test_commands.py +0 -0
  244. {bt_cli-0.4.68 → bt_cli-0.4.69}/tests/pws/test_quick_sessions.py +0 -0
  245. {bt_cli-0.4.68 → bt_cli-0.4.69}/tests/pws-quick-test-plan.md +0 -0
  246. {bt_cli-0.4.68 → bt_cli-0.4.69}/tests/pws-smoke-test.sh +0 -0
  247. {bt_cli-0.4.68 → bt_cli-0.4.69}/tests/secrets/__init__.py +0 -0
  248. {bt_cli-0.4.68 → bt_cli-0.4.69}/tests/secrets/test_client.py +0 -0
  249. {bt_cli-0.4.68 → bt_cli-0.4.69}/tests/secrets/test_commands.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.68**
3
+ BeyondTrust Platform CLI for Password Safe, Entitle, PRA, EPM Windows, EPM Linux, and the BeyondTrust Secrets API. **Version: 0.4.69**
4
4
 
5
5
  ## Setup
6
6
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: bt-cli
3
- Version: 0.4.68
3
+ Version: 0.4.69
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.68"
7
+ version = "0.4.69"
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.68"
3
+ __version__ = "0.4.69"
@@ -0,0 +1,72 @@
1
+ ---
2
+ name: session-analysis
3
+ description: Analyze a dumped Password Safe session (replay frames + keystrokes) into a timeline summary with a cost estimate. Use when the user points at a `session-<id>-replay` folder or asks to summarize/analyze a recorded RDP or SSH session.
4
+ ---
5
+
6
+ # Session Analysis
7
+
8
+ Turn a bt-cli session dump into a **timeline summary of what happened**, efficiently, with a **cost estimate**. Works for RDP (screen recording → frames) and SSH (keystroke transcript). This is interactive analysis — *you* (the model) read the frames/transcript; there is no LLM call inside the CLI.
9
+
10
+ ## The one rule: never read every frame
11
+
12
+ An RDP session sampled at ~1 frame per 2s is dozens of near-identical frames. Reading them all wastes tokens. Let the CLI pick the frames worth reading — the ones where the screen changed **and** the ones that render what the user typed — and read only those, guided by the per-frame `typed`/`reason` tags.
13
+
14
+ ## Recipe
15
+
16
+ ### 1. Prep with the CLI (does the heavy lifting)
17
+
18
+ ```bash
19
+ # If the user has a replay folder already:
20
+ bt pws sessions analyze --dir <folder>
21
+ # If they have the session id (reads ./session-<id>-replay):
22
+ bt pws sessions analyze <id> --model opus # or --model sonnet for a cheaper estimate
23
+ ```
24
+
25
+ `analyze` writes `analysis.json` into the folder and prints:
26
+ - **Who / what / when:** `user` (the initiator's real name + username, resolved from `UserID` via `/Users/{id}`), `user_email`, `account`@`system`, `start_time`. **Always name the human in the summary** — "UserID 219" is not an answer; "Amy Miller (amiller)" is.
27
+ - **RDP:** the **keyframes** to read. Each is tagged with a **reason** — `screen-change` (the picture moved) and/or `after-typing` (it renders the result of something the user typed). `after-typing` frames also carry the exact `typed` text.
28
+ - **SSH:** a pointer to the transcript (analyze it instead of frames).
29
+ - **`typed_bursts`** — typed input grouped into phrases with recording-relative offsets (`@14.9s "powershell"`), already anchored to the keyframes above.
30
+ - A **cost estimate**: `focused` (keyframes only) vs `all frames`.
31
+
32
+ If the user only has a folder (no live API access), `analyze --dir` still works — user name, keystrokes, and anchoring need the session metadata (`StartTime`/`UserID`), so offline it falls back to `screen-change` keyframes only, with a note. Keyframes + cost are still computed.
33
+
34
+ ### 2. Combine both signals — transcript AND frames
35
+
36
+ Don't treat this as either/or. The transcript gives you *verbatim text* the screen can't render cleanly; the frames give you *visual state* the transcript can't capture. Use each for what it's good at, and correlate them by timestamp (the CLI already did the correlation — `typed`/`typed_bursts` carry offsets, keystrokes carry `time`).
37
+
38
+ - **Always pull the transcript** when there is one: `bt pws sessions transcript <id>`. It's cheap.
39
+ - **SSH:** the transcript *is* the analysis — full input+output (typed commands `»` + terminal stdout). Read it; frames are rarely worth opening.
40
+ - **RDP:** the transcript is sparse (typed keys / window titles, often empty) but still adds color frames miss — the exact command typed, a window title, a value entered in a field. Fold it into the narrative alongside the frames.
41
+ - **Read the frames** (RDP): **read every keyframe the CLI listed — do not sub-sample them.** The CLI already culled the full recording down to the frames that matter; picking "6 of 37 to confirm the story" is how you miss events (a service restart, a dialog, a config change happen in the frames you skipped). If the full keyframe set is genuinely too expensive, raise `--threshold` and re-run so the *CLI* drops frames deliberately — never silently skip listed keyframes. Prioritize `after-typing` frames (the `typed` field tells you what to look for before you open the image), then read the `screen-change` frames for the mouse-driven actions keystrokes don't cover (menus, service start/stop, window closes).
42
+
43
+ ### 3. Anchoring is already done
44
+
45
+ The CLI has correlated keystrokes → frames for you (via the recording `StartTime`): the `typed` field on each keyframe and the `typed_bursts` list *are* the timeline anchors. Just narrate them — "at ~0:14 typed `powershell`, frame at 16s shows PowerShell 7 open". If RDP keystroke capture was off (bursts empty), fall back to reading every screen-change frame and inferring actions from the pictures.
46
+
47
+ ### 4. Write the summary
48
+
49
+ Produce:
50
+ - **Who** — the initiating user by name (from `user`/`user_email`), the account@system, and the time window. Note if the initiator is an API/service credential (e.g. `cli-<name>`) vs an interactive login — that distinction matters for audit.
51
+ - **Timeline table** — approximate time → event (app launched, window opened, command run, dialog shown, service changed, logoff).
52
+ - **Highlights an auditor cares about** — EPM/Defendpoint elevation or justification prompts, privileged tools (`dsa.msc`, `services.msc`, PowerShell), AD/account/OU changes, service start/stop, file transfers.
53
+ - **Security note** — explicitly state whether any credentials, passwords, tokens, or secret values were visible in the frames read.
54
+ - **Coverage & cost** — say how many of the listed keyframes you actually read (ideally all of them) so the reader knows the summary isn't under-sampled, and report the estimate from `analysis.json`.
55
+
56
+ ## Efficiency notes
57
+
58
+ - Two selection signals combine: PNG-size delta (`screen-change`, dependency-free) and keystroke anchoring (`after-typing`, when RDP keystroke capture is on). Anchoring is what makes the read *targeted* — it guarantees the frame that shows each typed action is included, even if its size barely moved.
59
+ - **Blind spot — mouse-driven, low-pixel-change actions.** A subtle action done with the mouse (a service going Running→Stopped is a few pixels of text, no keystroke) can slip past *both* signals: no `after-typing` anchor, and too small a PNG delta to trip `screen-change`. This is the classic "we missed the service restart." Mitigations: (a) **read all listed keyframes** — the restart's modal progress dialog usually *is* a keyframe, you just have to open it; (b) if you suspect a missed event in a quiet stretch, re-run `analyze --threshold 1.5` (or `1.0`) to surface smaller deltas; (c) for forensic certainty, `bt pws sessions replay <id> --all-frames --no-dedup` captures every recorded change. Under-sampling is usually the agent skipping listed keyframes, not the CLI mis-selecting them.
60
+ - If a session looks under-sampled (transitions missed), re-run `analyze --threshold 1.5` for more keyframes; over-sampled → raise it.
61
+ - Frame token cost ≈ `(width × height) / 750` per image (1024×768 ≈ ~1,050 tokens). The CLI's estimate already accounts for this.
62
+ - Pricing (per 1M tokens, for the estimate): Opus 4.8 $5/$25, Sonnet 4.6 $3/$15, Haiku 4.5 $1/$5. Use the `claude-api` skill for current numbers.
63
+
64
+ ## Scaling up (optional, not built)
65
+
66
+ For summarizing **many** sessions unattended, this interactive recipe doesn't scale — that would be a `bt pws sessions summarize` command calling the Claude API (vision) with the same keyframe/transcript inputs and the Batch API for volume. Mention it if the user wants automation; today, analysis is interactive.
67
+
68
+ ## Related
69
+
70
+ - `bt pws sessions replay <id>` — produce the frames (defaults to even coverage + dedup).
71
+ - `bt pws sessions transcript <id>` — the keystroke transcript (SSH gold; RDP sparse).
72
+ - `/pws` skill — Password Safe command reference.
@@ -762,6 +762,17 @@ def _build_keyframes(
762
762
  return keyframes
763
763
 
764
764
 
765
+ def _user_label(user: dict) -> str:
766
+ """Human label for a /Users record: 'First Last (username)' or best available."""
767
+ if not user:
768
+ return ""
769
+ full = " ".join(p for p in (user.get("FirstName"), user.get("LastName")) if p).strip()
770
+ handle = user.get("UserName") or user.get("EmailAddress") or ""
771
+ if full and handle:
772
+ return f"{full} ({handle})"
773
+ return full or handle or (f"UserID {user.get('UserID')}" if user.get("UserID") else "")
774
+
775
+
765
776
  @app.command("analyze")
766
777
  def session_analyze(
767
778
  session_id: int = typer.Argument(None, help="Session ID (defaults to reading ./session-<id>-replay)"),
@@ -805,18 +816,27 @@ def session_analyze(
805
816
  frames = idx.get("frames", [])
806
817
  sid = session_id or idx.get("session_id")
807
818
 
808
- # Pull keystrokes + session meta (best-effort; folder may be analyzed offline)
819
+ # Pull keystrokes + session meta + the initiating user (best-effort; folder
820
+ # may be analyzed offline with no API access).
809
821
  keystrokes: list[dict] = []
810
822
  meta: dict = {}
823
+ user: dict = {}
811
824
  if sid:
812
825
  try:
813
826
  with get_client() as client:
814
827
  client.authenticate()
815
828
  meta = client.get_session(sid)
816
829
  keystrokes = client.get_keystrokes(sid)
830
+ uid = meta.get("UserID")
831
+ if uid:
832
+ try:
833
+ user = client.get_user(uid)
834
+ except Exception:
835
+ pass # name resolution is a nicety; don't fail the analysis
817
836
  except Exception as e:
818
837
  if output != "json":
819
838
  print_warning(f"Could not pull keystrokes for session {sid} ({e}); continuing with frames only.")
839
+ user_label = _user_label(user)
820
840
 
821
841
  decoded_ks = [
822
842
  {"time": _fmt_marker(k.get("TimeMarker")), "type": _keystroke_type(k.get("Type")), "data": k.get("Data", "")}
@@ -854,6 +874,12 @@ def session_analyze(
854
874
  manifest = {
855
875
  "session_id": sid,
856
876
  "protocol": protocol,
877
+ "user_id": meta.get("UserID"),
878
+ "user": user_label,
879
+ "user_email": user.get("EmailAddress") if user else None,
880
+ "account": meta.get("ManagedAccountName"),
881
+ "system": meta.get("AssetName"),
882
+ "start_time": meta.get("StartTime"),
857
883
  "frame_dims": [w, h],
858
884
  "frame_count": len(frames),
859
885
  "keyframe_count": len(keyframes),
@@ -873,6 +899,9 @@ def session_analyze(
873
899
  acct = meta.get("ManagedAccountName", "?")
874
900
  sysn = meta.get("AssetName", "?")
875
901
  console.print(f"[bold]Session {sid}[/bold] {protocol} {acct}@{sysn}")
902
+ if user_label:
903
+ console.print(f" [green]Initiated by[/green] {rich_escape(user_label)}"
904
+ + (f" [dim]· {meta.get('StartTime')}[/dim]" if meta.get("StartTime") else ""))
876
905
  console.print(f" Frames: {len(frames)} ({w}x{h}) Keystroke events: {len(decoded_ks)}")
877
906
  if protocol.upper() == "SSH":
878
907
  console.print(" [green]SSH → analyze the transcript[/green] (cheap, full input+output):")
@@ -1,64 +0,0 @@
1
- ---
2
- name: session-analysis
3
- description: Analyze a dumped Password Safe session (replay frames + keystrokes) into a timeline summary with a cost estimate. Use when the user points at a `session-<id>-replay` folder or asks to summarize/analyze a recorded RDP or SSH session.
4
- ---
5
-
6
- # Session Analysis
7
-
8
- Turn a bt-cli session dump into a **timeline summary of what happened**, efficiently, with a **cost estimate**. Works for RDP (screen recording → frames) and SSH (keystroke transcript). This is interactive analysis — *you* (the model) read the frames/transcript; there is no LLM call inside the CLI.
9
-
10
- ## The one rule: never read every frame
11
-
12
- An RDP session sampled at ~1 frame per 2s is dozens of near-identical frames. Reading them all wastes tokens. Let the CLI pick the frames worth reading — the ones where the screen changed **and** the ones that render what the user typed — and read only those, guided by the per-frame `typed`/`reason` tags.
13
-
14
- ## Recipe
15
-
16
- ### 1. Prep with the CLI (does the heavy lifting)
17
-
18
- ```bash
19
- # If the user has a replay folder already:
20
- bt pws sessions analyze --dir <folder>
21
- # If they have the session id (reads ./session-<id>-replay):
22
- bt pws sessions analyze <id> --model opus # or --model sonnet for a cheaper estimate
23
- ```
24
-
25
- `analyze` writes `analysis.json` into the folder and prints:
26
- - **RDP:** the **keyframes** to read (a handful, not all). Each keyframe is tagged with a **reason** — `screen-change` (the picture moved) and/or `after-typing` (it renders the result of something the user typed). `after-typing` frames also carry the exact `typed` text.
27
- - **SSH:** a pointer to the transcript (analyze it instead of frames).
28
- - **`typed_bursts`** — typed input grouped into phrases with recording-relative offsets (`@14.9s "powershell"`), already anchored to the keyframes above.
29
- - A **cost estimate**: `focused` (keyframes only) vs `all frames`.
30
-
31
- If the user only has a folder (no live API access), `analyze --dir` still works — keystrokes need session metadata (`StartTime`) to anchor, so offline it falls back to `screen-change` keyframes only, with a note. Keyframes + cost are still computed.
32
-
33
- ### 2. Read the right inputs — targeted, in `analysis.json` order
34
-
35
- - **RDP:** `Read` only the keyframe PNGs the CLI listed. **Prioritize the `after-typing` frames** — they're where a user action produced a visible result (a launched app, a command's output, an EPM prompt). For each, the `typed` field tells you what to look for *before* you open the image, so you read with intent instead of scanning. The `screen-change`-only frames fill in transitions the keystrokes don't cover (mouse-driven actions: menus, service restarts, window closes). If a keyframe implies something happened between it and the next, that's fine — the summary is event-level, not frame-level.
36
- - **SSH:** run `bt pws sessions transcript <id>` and read that — it's the full input+output, far richer and cheaper than frames. Frames are rarely worth reading for SSH.
37
-
38
- ### 3. Anchoring is already done
39
-
40
- The CLI has correlated keystrokes → frames for you (via the recording `StartTime`): the `typed` field on each keyframe and the `typed_bursts` list *are* the timeline anchors. Just narrate them — "at ~0:14 typed `powershell`, frame at 16s shows PowerShell 7 open". If RDP keystroke capture was off (bursts empty), fall back to reading the screen-change frames and inferring actions from the pictures alone.
41
-
42
- ### 4. Write the summary
43
-
44
- Produce:
45
- - **Timeline table** — approximate time → event (app launched, window opened, command run, dialog shown, service changed, logoff).
46
- - **Highlights an auditor cares about** — EPM/Defendpoint elevation or justification prompts, privileged tools (`dsa.msc`, `services.msc`, PowerShell), AD/account/OU changes, service start/stop, file transfers.
47
- - **Security note** — explicitly state whether any credentials, passwords, tokens, or secret values were visible in the frames read.
48
- - **Cost** — report the estimate from `analysis.json` (and, if you want, the actual: images read × ~`(w*h)/750` tokens + output).
49
-
50
- ## Efficiency notes
51
-
52
- - Two selection signals combine: PNG-size delta (`screen-change`, dependency-free) and keystroke anchoring (`after-typing`, when RDP keystroke capture is on). Anchoring is what makes the read *targeted* — it guarantees the frame that shows each typed action is included, even if its size barely moved. If a session looks under-sampled (transitions missed), re-run `analyze --threshold 1.5` for more keyframes; over-sampled → raise it.
53
- - Frame token cost ≈ `(width × height) / 750` per image (1024×768 ≈ ~1,050 tokens). The CLI's estimate already accounts for this.
54
- - Pricing (per 1M tokens, for the estimate): Opus 4.8 $5/$25, Sonnet 4.6 $3/$15, Haiku 4.5 $1/$5. Use the `claude-api` skill for current numbers.
55
-
56
- ## Scaling up (optional, not built)
57
-
58
- For summarizing **many** sessions unattended, this interactive recipe doesn't scale — that would be a `bt pws sessions summarize` command calling the Claude API (vision) with the same keyframe/transcript inputs and the Batch API for volume. Mention it if the user wants automation; today, analysis is interactive.
59
-
60
- ## Related
61
-
62
- - `bt pws sessions replay <id>` — produce the frames (defaults to even coverage + dedup).
63
- - `bt pws sessions transcript <id>` — the keystroke transcript (SSH gold; RDP sparse).
64
- - `/pws` skill — Password Safe command reference.
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