bt-cli 0.4.61__tar.gz → 0.4.64__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.
- {bt_cli-0.4.61 → bt_cli-0.4.64}/CLAUDE.md +3 -1
- {bt_cli-0.4.61 → bt_cli-0.4.64}/PKG-INFO +1 -1
- {bt_cli-0.4.61 → bt_cli-0.4.64}/pyproject.toml +1 -1
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/__init__.py +1 -1
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/data/skills/pws/SKILL.md +35 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/pws/client/passwordsafe.py +60 -0
- bt_cli-0.4.64/src/bt_cli/pws/commands/sessions.py +624 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/tests/pws/test_client.py +58 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/tests/pws/test_commands.py +158 -1
- bt_cli-0.4.61/src/bt_cli/pws/commands/sessions.py +0 -243
- {bt_cli-0.4.61 → bt_cli-0.4.64}/.claude/skills/bt/SKILL.md +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/.claude/skills/entitle/SKILL.md +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/.claude/skills/epml/SKILL.md +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/.claude/skills/epmw/SKILL.md +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/.claude/skills/pra/SKILL.md +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/.claude/skills/pws/SKILL.md +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/.env.example +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/.github/workflows/ci.yml +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/.github/workflows/release.yml +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/.gitignore +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/README.md +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/assets/cli-help.png +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/assets/cli-output.png +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/bt-cli.spec +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/bt_entry.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/epml-clients-server-side-filters-plan.md +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/epml-implementation-plan.md +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/pf-implementation-plan.md +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/scripts/bt_entry.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/scripts/pf_onboard.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/scripts/sync-package-data.sh +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/cli.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/commands/__init__.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/commands/configure.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/commands/learn.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/commands/quick.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/core/__init__.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/core/auth.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/core/client.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/core/config.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/core/config_file.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/core/csv_utils.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/core/errors.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/core/output.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/core/prompts.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/core/rest_debug.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/data/CLAUDE.md +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/data/__init__.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/data/skills/bt/SKILL.md +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/data/skills/entitle/SKILL.md +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/data/skills/epml/SKILL.md +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/data/skills/epmw/SKILL.md +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/data/skills/pf/SKILL.md +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/data/skills/pra/SKILL.md +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/data/skills/secrets/SKILL.md +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/entitle/__init__.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/entitle/client/__init__.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/entitle/client/base.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/entitle/commands/__init__.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/entitle/commands/accounts.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/entitle/commands/applications.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/entitle/commands/auth.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/entitle/commands/bundles.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/entitle/commands/integrations.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/entitle/commands/permissions.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/entitle/commands/policies.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/entitle/commands/requests.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/entitle/commands/resources.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/entitle/commands/roles.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/entitle/commands/users.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/entitle/commands/workflows.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/entitle/models/__init__.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/entitle/models/bundle.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/entitle/models/common.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/entitle/models/integration.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/entitle/models/permission.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/entitle/models/policy.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/entitle/models/resource.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/entitle/models/role.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/entitle/models/user.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/entitle/models/workflow.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/epml/__init__.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/epml/client/__init__.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/epml/client/base.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/epml/commands/__init__.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/epml/commands/audit.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/epml/commands/auth.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/epml/commands/client_pkg.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/epml/commands/clients.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/epml/commands/external_apis.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/epml/commands/hosts.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/epml/commands/iolog.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/epml/commands/license.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/epml/commands/quick.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/epml/commands/rbp_cmdgrps.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/epml/commands/rbp_entitlement.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/epml/commands/rbp_hostgrps.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/epml/commands/rbp_policy.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/epml/commands/rbp_roles.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/epml/commands/rbp_tests.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/epml/commands/rbp_tmdategrps.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/epml/commands/rbp_tx.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/epml/commands/rbp_usergrps.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/epml/commands/settings.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/epml/commands/siems.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/epml/commands/users.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/epml/models/__init__.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/epmw/__init__.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/epmw/client/__init__.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/epmw/client/base.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/epmw/commands/__init__.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/epmw/commands/audits.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/epmw/commands/auth.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/epmw/commands/computers.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/epmw/commands/events.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/epmw/commands/groups.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/epmw/commands/policies.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/epmw/commands/quick.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/epmw/commands/requests.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/epmw/commands/roles.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/epmw/commands/tasks.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/epmw/commands/users.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/epmw/models/__init__.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/pf/__init__.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/pf/client/__init__.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/pf/client/base.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/pf/commands/__init__.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/pf/commands/auth.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/pf/commands/machines.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/pf/commands/tokens.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/pf/commands/user.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/pf/commands/users.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/pra/__init__.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/pra/client/__init__.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/pra/client/base.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/pra/commands/__init__.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/pra/commands/auth.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/pra/commands/group_policies.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/pra/commands/import_export.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/pra/commands/jump_clients.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/pra/commands/jump_groups.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/pra/commands/jump_items.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/pra/commands/jumpoints.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/pra/commands/policies.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/pra/commands/quick.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/pra/commands/teams.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/pra/commands/users.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/pra/commands/vault.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/pra/models/__init__.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/pra/models/common.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/pra/models/group_policy.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/pra/models/jump_client.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/pra/models/jump_group.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/pra/models/jump_item.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/pra/models/jumpoint.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/pra/models/team.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/pra/models/user.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/pra/models/vault.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/pws/__init__.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/pws/client/__init__.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/pws/client/base.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/pws/client/beyondinsight.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/pws/commands/__init__.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/pws/commands/accounts.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/pws/commands/assets.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/pws/commands/attributes.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/pws/commands/audit.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/pws/commands/auth.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/pws/commands/clouds.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/pws/commands/config.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/pws/commands/credentials.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/pws/commands/databases.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/pws/commands/directories.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/pws/commands/functional.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/pws/commands/import_export.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/pws/commands/platforms.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/pws/commands/quick.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/pws/commands/search.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/pws/commands/secrets.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/pws/commands/systems.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/pws/commands/users.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/pws/commands/workgroups.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/pws/config.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/pws/models/__init__.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/pws/models/account.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/pws/models/asset.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/pws/models/common.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/pws/models/system.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/secrets/__init__.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/secrets/client/__init__.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/secrets/client/base.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/secrets/commands/__init__.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/secrets/commands/_hints.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/secrets/commands/auth.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/secrets/commands/dynamic.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/secrets/commands/folders.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/secrets/commands/integrations.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/secrets/commands/leases.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/secrets/commands/static.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/src/bt_cli/secrets/models/__init__.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/tests/__init__.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/tests/conftest.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/tests/core/__init__.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/tests/core/test_auth.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/tests/core/test_config.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/tests/core/test_config_file.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/tests/core/test_errors.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/tests/core/test_output.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/tests/core/test_rest_debug.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/tests/entitle/__init__.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/tests/entitle/test_client.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/tests/entitle/test_commands.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/tests/entitle-smoke-test.sh +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/tests/epml/__init__.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/tests/epml/test_client.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/tests/epml/test_commands.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/tests/epmw/__init__.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/tests/epmw/test_client.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/tests/epmw/test_commands.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/tests/epmw-quick-test-plan.md +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/tests/fixtures/__init__.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/tests/fixtures/responses.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/tests/integration/__init__.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/tests/integration/conftest.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/tests/integration/helpers.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/tests/integration/test_entitle_integration.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/tests/integration/test_epmw_integration.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/tests/integration/test_epmw_lifecycle.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/tests/integration/test_pra_integration.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/tests/integration/test_pra_lifecycle.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/tests/integration/test_pws_integration.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/tests/integration/test_pws_lifecycle.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/tests/pf/__init__.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/tests/pf/test_client.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/tests/pf/test_commands.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/tests/pra/__init__.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/tests/pra/test_client.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/tests/pra/test_commands.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/tests/pra/test_jump_item_fixes.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/tests/pra-smoke-test.sh +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/tests/pra-test-plan.md +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/tests/pws/__init__.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/tests/pws/test_quick_sessions.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/tests/pws-quick-test-plan.md +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/tests/pws-smoke-test.sh +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/tests/secrets/__init__.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/tests/secrets/test_client.py +0 -0
- {bt_cli-0.4.61 → bt_cli-0.4.64}/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.
|
|
3
|
+
BeyondTrust Platform CLI for Password Safe, Entitle, PRA, EPM Windows, EPM Linux, and the BeyondTrust Secrets API. **Version: 0.4.64**
|
|
4
4
|
|
|
5
5
|
## Setup
|
|
6
6
|
|
|
@@ -87,6 +87,8 @@ PASSWORD=$(bt pws quick checkout -s server -a admin --raw)
|
|
|
87
87
|
- **PRA vault `associate -t protocol_tunnel` is MSSQL-only** (live-verified): server rejects psql/mysql/tcp/k8s with "Only SQL Server Tunnels are allowed" — use tag/criteria association for those. **TCP `--ports` needs listen;destination *pairs*** (even count; odd → 400 "number of parameters is invalid"). Jumpoint listing is `bt pra jumpoints list` (plural).
|
|
88
88
|
- **PWS `/Sessions` ignores `limit`/`offset`** and returns the full session *history* every call — never `paginate()` it (infinite loop); `list_sessions` does a single GET and `bt pws sessions list` caps display client-side. `Status` codes (live-confirmed): 0=Not started, 1=Active, 2=Completed, 5=Locked, 7=Terminated; `--active`=Status 1. Active sessions have `EndTime`==`StartTime` (placeholder, not null) — identify live sessions by Status, not timestamps. **`terminate` = `POST /Sessions/{id}/Terminate`** (symmetric w/ `/Lock`); `/Sessions/{id}` is GET/PUT only (DELETE → 405).
|
|
89
89
|
- **PWS SSH/RDP session establishment = `POST /Requests/{id}/Sessions`** (`{"SessionType":"SSH"|"RDP"}`), NOT `POST /Sessions` (collection is GET-only → 405). Create the request with matching `AccessType` first. **SSH** returns a ready `Command` (`ssh -p 4422 <ticket>@<proxyhost>`) + `Link`; **RDP** returns `Host`/`Port`/`Ticket` only — the `.rdp` is built client-side (`full address` = proxy `Host:Port`, `username` = `Ticket`); there's no server-side launch file (`/Sessions/RDPFile` is audit-playback → 403). Ticket is the proxy username; password is proxy-injected. `bt pws quick checkout --access-type ssh|rdp` and `bt pws quick ssh`/`quick rdp` (with `--launch`); session commands do **not** auto-checkin.
|
|
90
|
+
- **PWS session keystroke transcript = `GET /Sessions/{id}/Keystrokes`** → events `{KeystrokeID, TimeMarker (epoch ms), Type, Data}`. **Type 1 = user input** (typed keys; special keys as glyphs `⌫`/`→`/`↑`), **Type 2 = terminal output** (SSH stdout). SSH captures a full input+output transcript; **RDP is sparse** (typed keys / window events only, off by default → often empty). Surfaced as `bt pws sessions transcript <id>` (default = time-ordered readable transcript; `--input-only`; text/json/csv). Text trail, not the visual recording. (There is **no** `bt pws sessions export` — session *metadata* evidence is `bt pws audit export`.)
|
|
91
|
+
- **PWS session recording download = headless replay under `/pbsm/replay`** (there is **no** video-export API — `?video/?mp4/?webm` return JSON, not media). Flow: `GET /Sessions/{id}` for `Token`+`RecordKey`+`Protocol` → `POST /pbsm/replay` `{id:Token, record_key, protocol:"RDP"|"SSH", headless:true}` → `PUT /pbsm/replay/{rid}` `{offset:<ms>}` (seek) → `GET /pbsm/replay/{rid}?png=<scale>` (one PNG per offset) → `DELETE /pbsm/replay/{rid}`. **Warm-up:** the first `PUT` 404s (`"no such file or directory"`) until the renderer loads — retry ~0.5s. The seek response is state `{offset, next, eof, duration}`; **`next` = next recorded frame's offset**, enabling event-driven stepping. Surfaced as `bt pws sessions replay <id>` (event-driven default; `--fps`/`--interval` fixed sampling; `--from/--to`, `--scale`, `--max-frames`; `--mp4` stitches via ffmpeg, else prints the command + install hint). Always `DELETE`s the replay in `finally`.
|
|
90
92
|
- Entitle permissions: Use server-side filters (`--resource`, `--user`, `--integration`) - dataset can be 20k+ records
|
|
91
93
|
- Entitle users: Use `--all` flag only when needed - default fetches first page only
|
|
92
94
|
- Windows builds: Rich must be pinned to `<14.0.0` (Rich 14 has PyInstaller unicode issues)
|
|
@@ -195,8 +195,43 @@ bt pws sessions suspend <session_id>
|
|
|
195
195
|
# Terminate = end permanently (cannot be resumed) - prompts unless --force
|
|
196
196
|
bt pws sessions terminate <session_id>
|
|
197
197
|
bt pws sessions terminate <session_id> --force
|
|
198
|
+
|
|
199
|
+
# Transcript = recorded keystrokes for a session (SSH input+output, RDP sparse)
|
|
200
|
+
bt pws sessions transcript <session_id> # readable transcript (default)
|
|
201
|
+
bt pws sessions transcript <session_id> --input-only # just the commands typed
|
|
202
|
+
bt pws sessions transcript <session_id> -o json | jq
|
|
203
|
+
bt pws sessions transcript <session_id> --file s.txt
|
|
204
|
+
bt pws sessions transcript <session_id> --file s.csv --format csv
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
`transcript` pulls `GET /Sessions/{id}/Keystrokes`: events `{KeystrokeID,
|
|
208
|
+
TimeMarker (epoch ms), Type, Data}` where **Type 1 = user input** (typed keys,
|
|
209
|
+
special keys as glyphs like `⌫`/`→`) and **Type 2 = terminal output** (SSH
|
|
210
|
+
stdout). The default view reconstructs a time-ordered transcript (input marked
|
|
211
|
+
`»`, output verbatim). SSH sessions capture a full transcript; **RDP capture is
|
|
212
|
+
sparse** (typed keys / window events only, and only when keystroke logging is
|
|
213
|
+
enabled - many RDP sessions have none). This is the text trail, not the visual
|
|
214
|
+
recording. For session *metadata* evidence, use `bt pws audit export`.
|
|
215
|
+
|
|
216
|
+
```bash
|
|
217
|
+
# Download the VISUAL recording as PNG frames (RDP screen / SSH terminal)
|
|
218
|
+
bt pws sessions replay <session_id> # event-driven frames + index.json
|
|
219
|
+
bt pws sessions replay <session_id> --mp4 # also stitch an MP4 (needs ffmpeg)
|
|
220
|
+
bt pws sessions replay <session_id> --fps 1 --to 120 # 1 fps for the first 2 minutes
|
|
221
|
+
bt pws sessions replay <session_id> --out ./review --scale 1 --max-frames 500
|
|
198
222
|
```
|
|
199
223
|
|
|
224
|
+
`replay` drives the headless renderer at `/pbsm/replay` (there is **no**
|
|
225
|
+
video-export API): create replay → seek by offset → pull one PNG per frame →
|
|
226
|
+
delete. By default it steps **event-driven** (following the replay state's
|
|
227
|
+
`next`/`eof` fields, one frame per recorded change); `--fps`/`--interval` force
|
|
228
|
+
fixed-rate sampling. **It's one network round-trip per frame, so long sessions
|
|
229
|
+
are slow** - bound them with `--fps`, `--from/--to`, or `--max-frames` (default
|
|
230
|
+
3600). `--mp4` stitches with ffmpeg (H.264); if ffmpeg is missing it prints the
|
|
231
|
+
exact command + install instructions and leaves the frames. Works for RDP
|
|
232
|
+
(screen) and SSH (rendered terminal). Gotcha: the renderer needs ~0.5s warm-up
|
|
233
|
+
(first seek 404s), handled automatically.
|
|
234
|
+
|
|
200
235
|
**Gotchas:**
|
|
201
236
|
- `/Sessions` returns session **history**, not just active sessions, and **ignores `limit`/`offset`** (returns the full set every call - the client does a single GET, the CLI caps display client-side). Use `--active` for the live ones.
|
|
202
237
|
- `Status` codes confirmed live: **0 = Not started, 1 = Active, 2 = Completed, 5 = Locked, 7 = Terminated** (3/9 are other terminal states). `--active` filters on Status == 1.
|
|
@@ -594,6 +594,66 @@ class PasswordSafeMixin:
|
|
|
594
594
|
"""
|
|
595
595
|
return self.get(f"/Sessions/{session_id}")
|
|
596
596
|
|
|
597
|
+
def get_keystrokes(self: "PasswordSafeClient", session_id: int) -> list[dict[str, Any]]:
|
|
598
|
+
"""Get recorded keystrokes for a session.
|
|
599
|
+
|
|
600
|
+
GET /Sessions/{id}/Keystrokes. Each event is
|
|
601
|
+
{KeystrokeID, SessionID, TimeMarker (epoch ms), Type, Data}. Type 1 =
|
|
602
|
+
user input (typed keys), Type 2 = terminal output (SSH). SSH sessions
|
|
603
|
+
capture a full transcript; RDP capture is sparse (often empty).
|
|
604
|
+
|
|
605
|
+
Returns:
|
|
606
|
+
List of keystroke events (empty list if none recorded).
|
|
607
|
+
"""
|
|
608
|
+
result = self.get(f"/Sessions/{session_id}/Keystrokes")
|
|
609
|
+
if isinstance(result, list):
|
|
610
|
+
return result
|
|
611
|
+
if isinstance(result, dict) and "Data" in result:
|
|
612
|
+
return result["Data"]
|
|
613
|
+
return []
|
|
614
|
+
|
|
615
|
+
# --- Session replay (headless renderer under /pbsm/replay) ---
|
|
616
|
+
# There is no video-export endpoint: you create a replay, seek by offset,
|
|
617
|
+
# and pull one PNG per offset, then stitch client-side. The seek response
|
|
618
|
+
# carries {offset, next, eof, duration} - `next` is the next recorded
|
|
619
|
+
# frame's offset (ms), enabling event-driven stepping.
|
|
620
|
+
|
|
621
|
+
def create_replay(
|
|
622
|
+
self: "PasswordSafeClient", token: str, record_key: str, protocol: str
|
|
623
|
+
) -> dict[str, Any]:
|
|
624
|
+
"""Create a headless replay session. Returns dict with `id` (replayId)."""
|
|
625
|
+
return self.post(
|
|
626
|
+
"/pbsm/replay",
|
|
627
|
+
json={"id": token, "record_key": record_key, "protocol": protocol, "headless": True},
|
|
628
|
+
)
|
|
629
|
+
|
|
630
|
+
def seek_replay(self: "PasswordSafeClient", replay_id: str, offset_ms: int) -> dict[str, Any]:
|
|
631
|
+
"""Seek the replay to offset (ms from start). Returns replay state.
|
|
632
|
+
|
|
633
|
+
Note: immediately after create the renderer isn't ready and this 404s
|
|
634
|
+
("no such file or directory") - callers should retry with a short delay.
|
|
635
|
+
"""
|
|
636
|
+
return self.put(f"/pbsm/replay/{replay_id}", json={"offset": offset_ms})
|
|
637
|
+
|
|
638
|
+
def get_replay_frame(self: "PasswordSafeClient", replay_id: str, scale: int = 1) -> bytes:
|
|
639
|
+
"""Fetch the PNG frame at the current offset. Returns raw PNG bytes.
|
|
640
|
+
|
|
641
|
+
Uses the underlying client directly so binary content isn't coerced to
|
|
642
|
+
text. If the server returns non-PNG (e.g. state JSON when not ready),
|
|
643
|
+
the raw bytes are returned as-is for the caller to validate.
|
|
644
|
+
"""
|
|
645
|
+
client = self._ensure_client()
|
|
646
|
+
headers = self._get_auth_headers()
|
|
647
|
+
response = client.request(
|
|
648
|
+
"GET", f"/pbsm/replay/{replay_id}", params={"png": scale}, headers=headers
|
|
649
|
+
)
|
|
650
|
+
response.raise_for_status()
|
|
651
|
+
return response.content
|
|
652
|
+
|
|
653
|
+
def delete_replay(self: "PasswordSafeClient", replay_id: str) -> dict[str, Any]:
|
|
654
|
+
"""Terminate a replay session (always call this to free the renderer)."""
|
|
655
|
+
return self.delete(f"/pbsm/replay/{replay_id}")
|
|
656
|
+
|
|
597
657
|
def lock_session(self: "PasswordSafeClient", session_id: int) -> dict[str, Any]:
|
|
598
658
|
"""Lock an active session.
|
|
599
659
|
|