bt-cli 0.4.50__tar.gz → 0.4.52__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.50 → bt_cli-0.4.52}/CLAUDE.md +1 -1
- {bt_cli-0.4.50 → bt_cli-0.4.52}/PKG-INFO +1 -1
- {bt_cli-0.4.50 → bt_cli-0.4.52}/pyproject.toml +1 -1
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/__init__.py +1 -1
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/core/errors.py +13 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/core/prompts.py +16 -2
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/pws/client/passwordsafe.py +29 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/pws/commands/accounts.py +166 -1
- {bt_cli-0.4.50 → bt_cli-0.4.52}/.claude/skills/bt/SKILL.md +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/.claude/skills/entitle/SKILL.md +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/.claude/skills/epml/SKILL.md +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/.claude/skills/epmw/SKILL.md +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/.claude/skills/pra/SKILL.md +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/.claude/skills/pws/SKILL.md +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/.env.example +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/.github/workflows/ci.yml +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/.github/workflows/release.yml +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/.gitignore +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/README.md +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/assets/cli-help.png +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/assets/cli-output.png +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/bt-cli.spec +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/bt_entry.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/epml-clients-server-side-filters-plan.md +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/epml-implementation-plan.md +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/pf-implementation-plan.md +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/scripts/bt_entry.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/scripts/pf_onboard.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/scripts/sync-package-data.sh +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/cli.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/commands/__init__.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/commands/configure.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/commands/learn.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/commands/quick.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/core/__init__.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/core/auth.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/core/client.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/core/config.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/core/config_file.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/core/csv_utils.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/core/output.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/core/rest_debug.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/data/CLAUDE.md +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/data/__init__.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/data/skills/bt/SKILL.md +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/data/skills/entitle/SKILL.md +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/data/skills/epml/SKILL.md +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/data/skills/epmw/SKILL.md +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/data/skills/pra/SKILL.md +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/data/skills/pws/SKILL.md +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/data/skills/secrets/SKILL.md +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/entitle/__init__.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/entitle/client/__init__.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/entitle/client/base.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/entitle/commands/__init__.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/entitle/commands/accounts.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/entitle/commands/applications.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/entitle/commands/auth.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/entitle/commands/bundles.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/entitle/commands/integrations.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/entitle/commands/permissions.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/entitle/commands/policies.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/entitle/commands/requests.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/entitle/commands/resources.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/entitle/commands/roles.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/entitle/commands/users.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/entitle/commands/workflows.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/entitle/models/__init__.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/entitle/models/bundle.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/entitle/models/common.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/entitle/models/integration.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/entitle/models/permission.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/entitle/models/policy.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/entitle/models/resource.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/entitle/models/role.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/entitle/models/user.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/entitle/models/workflow.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/epml/__init__.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/epml/client/__init__.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/epml/client/base.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/epml/commands/__init__.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/epml/commands/audit.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/epml/commands/auth.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/epml/commands/client_pkg.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/epml/commands/clients.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/epml/commands/external_apis.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/epml/commands/hosts.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/epml/commands/iolog.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/epml/commands/license.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/epml/commands/quick.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/epml/commands/rbp_cmdgrps.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/epml/commands/rbp_entitlement.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/epml/commands/rbp_hostgrps.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/epml/commands/rbp_policy.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/epml/commands/rbp_roles.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/epml/commands/rbp_tests.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/epml/commands/rbp_tmdategrps.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/epml/commands/rbp_tx.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/epml/commands/rbp_usergrps.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/epml/commands/settings.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/epml/commands/siems.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/epml/commands/users.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/epml/models/__init__.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/epmw/__init__.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/epmw/client/__init__.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/epmw/client/base.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/epmw/commands/__init__.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/epmw/commands/audits.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/epmw/commands/auth.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/epmw/commands/computers.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/epmw/commands/events.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/epmw/commands/groups.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/epmw/commands/policies.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/epmw/commands/quick.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/epmw/commands/requests.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/epmw/commands/roles.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/epmw/commands/tasks.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/epmw/commands/users.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/epmw/models/__init__.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/pra/__init__.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/pra/client/__init__.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/pra/client/base.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/pra/commands/__init__.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/pra/commands/auth.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/pra/commands/group_policies.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/pra/commands/import_export.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/pra/commands/jump_clients.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/pra/commands/jump_groups.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/pra/commands/jump_items.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/pra/commands/jumpoints.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/pra/commands/policies.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/pra/commands/quick.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/pra/commands/teams.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/pra/commands/users.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/pra/commands/vault.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/pra/models/__init__.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/pra/models/common.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/pra/models/group_policy.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/pra/models/jump_client.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/pra/models/jump_group.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/pra/models/jump_item.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/pra/models/jumpoint.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/pra/models/team.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/pra/models/user.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/pra/models/vault.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/pws/__init__.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/pws/client/__init__.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/pws/client/base.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/pws/client/beyondinsight.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/pws/commands/__init__.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/pws/commands/assets.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/pws/commands/attributes.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/pws/commands/auth.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/pws/commands/clouds.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/pws/commands/config.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/pws/commands/credentials.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/pws/commands/databases.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/pws/commands/directories.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/pws/commands/functional.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/pws/commands/import_export.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/pws/commands/platforms.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/pws/commands/quick.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/pws/commands/search.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/pws/commands/secrets.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/pws/commands/systems.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/pws/commands/users.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/pws/commands/workgroups.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/pws/config.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/pws/models/__init__.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/pws/models/account.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/pws/models/asset.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/pws/models/common.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/pws/models/system.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/secrets/__init__.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/secrets/client/__init__.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/secrets/client/base.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/secrets/commands/__init__.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/secrets/commands/_hints.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/secrets/commands/auth.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/secrets/commands/dynamic.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/secrets/commands/folders.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/secrets/commands/integrations.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/secrets/commands/leases.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/secrets/commands/static.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/src/bt_cli/secrets/models/__init__.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/tests/__init__.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/tests/conftest.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/tests/core/__init__.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/tests/core/test_auth.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/tests/core/test_config.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/tests/core/test_errors.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/tests/core/test_rest_debug.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/tests/entitle/__init__.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/tests/entitle/test_client.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/tests/entitle/test_commands.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/tests/entitle-smoke-test.sh +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/tests/epml/__init__.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/tests/epml/test_client.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/tests/epml/test_commands.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/tests/epmw/__init__.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/tests/epmw/test_client.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/tests/epmw/test_commands.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/tests/epmw-quick-test-plan.md +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/tests/fixtures/__init__.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/tests/fixtures/responses.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/tests/integration/__init__.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/tests/integration/conftest.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/tests/integration/helpers.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/tests/integration/test_entitle_integration.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/tests/integration/test_epmw_integration.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/tests/integration/test_epmw_lifecycle.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/tests/integration/test_pra_integration.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/tests/integration/test_pra_lifecycle.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/tests/integration/test_pws_integration.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/tests/integration/test_pws_lifecycle.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/tests/pra/__init__.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/tests/pra/test_client.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/tests/pra/test_commands.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/tests/pra-smoke-test.sh +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/tests/pra-test-plan.md +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/tests/pws/__init__.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/tests/pws/test_client.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/tests/pws/test_commands.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/tests/pws-quick-test-plan.md +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/tests/pws-smoke-test.sh +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/tests/secrets/__init__.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/tests/secrets/test_client.py +0 -0
- {bt_cli-0.4.50 → bt_cli-0.4.52}/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.52**
|
|
4
4
|
|
|
5
5
|
## Setup
|
|
6
6
|
|
|
@@ -165,6 +165,19 @@ def get_http_error_message(error: httpx.HTTPStatusError) -> str:
|
|
|
165
165
|
# Response may not be JSON
|
|
166
166
|
pass
|
|
167
167
|
|
|
168
|
+
# plain-text bodies (e.g. PWS 400 responses that aren't JSON)
|
|
169
|
+
if not detail:
|
|
170
|
+
try:
|
|
171
|
+
raw = error.response.text
|
|
172
|
+
if raw:
|
|
173
|
+
orig_len = len(raw)
|
|
174
|
+
sanitized = sanitize_error_message(raw[:300])
|
|
175
|
+
if sanitized:
|
|
176
|
+
detail = sanitized + ("..." if orig_len > 300 else "")
|
|
177
|
+
return f"{base_message}: {detail}"
|
|
178
|
+
except Exception:
|
|
179
|
+
pass
|
|
180
|
+
|
|
168
181
|
if detail:
|
|
169
182
|
# Additional regex sanitization for any patterns in the extracted detail
|
|
170
183
|
detail = sanitize_error_message(str(detail))
|
|
@@ -1,16 +1,24 @@
|
|
|
1
1
|
"""Reusable interactive prompts for CLI commands."""
|
|
2
2
|
|
|
3
|
+
import re
|
|
3
4
|
from typing import Any, Callable, Optional, TypeVar
|
|
4
5
|
|
|
5
6
|
import typer
|
|
6
7
|
from rich.console import Console
|
|
7
8
|
|
|
9
|
+
_ANSI_ESCAPE = re.compile(r"\x1b(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])")
|
|
10
|
+
|
|
8
11
|
console = Console()
|
|
9
12
|
|
|
10
13
|
T = TypeVar("T")
|
|
11
14
|
D = TypeVar("D")
|
|
12
15
|
|
|
13
16
|
|
|
17
|
+
def _clean_str(raw: Any) -> str:
|
|
18
|
+
"""Strip ANSI escape sequences from a prompted string value."""
|
|
19
|
+
return _ANSI_ESCAPE.sub("", raw).strip()
|
|
20
|
+
|
|
21
|
+
|
|
14
22
|
def prompt_if_missing(
|
|
15
23
|
value: Optional[T],
|
|
16
24
|
prompt_text: str,
|
|
@@ -27,7 +35,10 @@ def prompt_if_missing(
|
|
|
27
35
|
The value (either original or prompted)
|
|
28
36
|
"""
|
|
29
37
|
if value is None or value == "":
|
|
30
|
-
|
|
38
|
+
raw = typer.prompt(prompt_text, type=value_type)
|
|
39
|
+
if value_type is str:
|
|
40
|
+
return _clean_str(raw) # type: ignore[return-value]
|
|
41
|
+
return raw
|
|
31
42
|
return value
|
|
32
43
|
|
|
33
44
|
|
|
@@ -57,7 +68,10 @@ def prompt_from_list(
|
|
|
57
68
|
item_id = item.get(id_key, "")
|
|
58
69
|
item_name = item.get(name_key, "Unknown")
|
|
59
70
|
console.print(f" {item_id}: {item_name}")
|
|
60
|
-
|
|
71
|
+
raw = typer.prompt(prompt_text, type=value_type)
|
|
72
|
+
if value_type is str:
|
|
73
|
+
return _clean_str(raw) # type: ignore[return-value]
|
|
74
|
+
return raw
|
|
61
75
|
|
|
62
76
|
|
|
63
77
|
def prompt_choice(
|
|
@@ -80,6 +80,35 @@ class PasswordSafeMixin:
|
|
|
80
80
|
"""
|
|
81
81
|
return self.get(f"/ManagedAccounts/{account_id}")
|
|
82
82
|
|
|
83
|
+
# =========================================================================
|
|
84
|
+
# Synced Accounts
|
|
85
|
+
# =========================================================================
|
|
86
|
+
|
|
87
|
+
def list_synced_accounts(
|
|
88
|
+
self: "PasswordSafeClient", account_id: int
|
|
89
|
+
) -> list[dict[str, Any]]:
|
|
90
|
+
"""List accounts synced to a managed account."""
|
|
91
|
+
result = self.get(f"/ManagedAccounts/{account_id}/SyncedAccounts")
|
|
92
|
+
return result if isinstance(result, list) else []
|
|
93
|
+
|
|
94
|
+
def add_synced_account(
|
|
95
|
+
self: "PasswordSafeClient", account_id: int, synced_account_id: int
|
|
96
|
+
) -> dict[str, Any]:
|
|
97
|
+
"""Add a synced account relationship."""
|
|
98
|
+
return self.post(f"/ManagedAccounts/{account_id}/SyncedAccounts/{synced_account_id}")
|
|
99
|
+
|
|
100
|
+
def remove_synced_account(
|
|
101
|
+
self: "PasswordSafeClient", account_id: int, synced_account_id: int
|
|
102
|
+
) -> None:
|
|
103
|
+
"""Remove a specific synced account."""
|
|
104
|
+
self.delete(f"/ManagedAccounts/{account_id}/SyncedAccounts/{synced_account_id}")
|
|
105
|
+
|
|
106
|
+
def remove_all_synced_accounts(
|
|
107
|
+
self: "PasswordSafeClient", account_id: int
|
|
108
|
+
) -> None:
|
|
109
|
+
"""Remove all synced accounts from a managed account."""
|
|
110
|
+
self.delete(f"/ManagedAccounts/{account_id}/SyncedAccounts")
|
|
111
|
+
|
|
83
112
|
def get_managed_account_by_name(
|
|
84
113
|
self: "PasswordSafeClient",
|
|
85
114
|
system_name: str,
|
|
@@ -8,7 +8,7 @@ import typer
|
|
|
8
8
|
from rich.console import Console
|
|
9
9
|
from rich.table import Table
|
|
10
10
|
|
|
11
|
-
from ...core.output import print_api_error
|
|
11
|
+
from ...core.output import print_api_error, print_success
|
|
12
12
|
from ..client.base import get_client
|
|
13
13
|
|
|
14
14
|
app = typer.Typer(no_args_is_help=True, help="""Manage managed accounts in Password Safe.
|
|
@@ -370,3 +370,168 @@ def rotate_password(
|
|
|
370
370
|
except Exception as e:
|
|
371
371
|
print_api_error(e, "manage accounts")
|
|
372
372
|
raise typer.Exit(1)
|
|
373
|
+
|
|
374
|
+
|
|
375
|
+
# =============================================================================
|
|
376
|
+
# Synced Accounts
|
|
377
|
+
# =============================================================================
|
|
378
|
+
|
|
379
|
+
@app.command("synced-list")
|
|
380
|
+
def synced_list(
|
|
381
|
+
account_id: int = typer.Argument(..., help="Parent managed account ID"),
|
|
382
|
+
output: str = typer.Option("table", "--output", "-o", help="Output format: table or json"),
|
|
383
|
+
) -> None:
|
|
384
|
+
"""List accounts synced to a managed account.
|
|
385
|
+
|
|
386
|
+
Examples:
|
|
387
|
+
bt pws accounts synced-list 14
|
|
388
|
+
bt pws accounts synced-list 14 -o json
|
|
389
|
+
"""
|
|
390
|
+
try:
|
|
391
|
+
with get_client() as client:
|
|
392
|
+
client.authenticate()
|
|
393
|
+
accounts = client.list_synced_accounts(account_id)
|
|
394
|
+
|
|
395
|
+
if output == "json":
|
|
396
|
+
console.print_json(json.dumps(accounts, default=str))
|
|
397
|
+
elif accounts:
|
|
398
|
+
table = Table(title=f"Synced Accounts (parent ID: {account_id})")
|
|
399
|
+
table.add_column("Acct ID", style="cyan", no_wrap=True)
|
|
400
|
+
table.add_column("Account Name", style="green")
|
|
401
|
+
table.add_column("Domain", style="yellow")
|
|
402
|
+
table.add_column("Sys ID", style="blue", no_wrap=True)
|
|
403
|
+
table.add_column("Description", style="dim")
|
|
404
|
+
for a in accounts:
|
|
405
|
+
table.add_row(
|
|
406
|
+
str(a.get("ManagedAccountID", "")),
|
|
407
|
+
a.get("AccountName", ""),
|
|
408
|
+
a.get("DomainName") or "-",
|
|
409
|
+
str(a.get("ManagedSystemID", "")),
|
|
410
|
+
(a.get("Description") or "-")[:50],
|
|
411
|
+
)
|
|
412
|
+
console.print(table)
|
|
413
|
+
else:
|
|
414
|
+
console.print("[yellow]No synced accounts.[/yellow]")
|
|
415
|
+
|
|
416
|
+
except httpx.HTTPStatusError as e:
|
|
417
|
+
print_api_error(e, "synced-list")
|
|
418
|
+
raise typer.Exit(1)
|
|
419
|
+
except httpx.RequestError as e:
|
|
420
|
+
print_api_error(e, "synced-list")
|
|
421
|
+
raise typer.Exit(1)
|
|
422
|
+
except Exception as e:
|
|
423
|
+
print_api_error(e, "synced-list")
|
|
424
|
+
raise typer.Exit(1)
|
|
425
|
+
|
|
426
|
+
|
|
427
|
+
@app.command("synced-add")
|
|
428
|
+
def synced_add(
|
|
429
|
+
account_id: int = typer.Argument(..., help="Parent managed account ID"),
|
|
430
|
+
synced_account_id: int = typer.Argument(..., help="Account ID to sync"),
|
|
431
|
+
output: str = typer.Option("table", "--output", "-o", help="Output format: table or json"),
|
|
432
|
+
) -> None:
|
|
433
|
+
"""Add a synced account relationship.
|
|
434
|
+
|
|
435
|
+
Synced accounts share password changes with the parent account.
|
|
436
|
+
|
|
437
|
+
Examples:
|
|
438
|
+
bt pws accounts synced-add 14 25
|
|
439
|
+
bt pws accounts synced-add 14 25 -o json
|
|
440
|
+
"""
|
|
441
|
+
try:
|
|
442
|
+
with get_client() as client:
|
|
443
|
+
client.authenticate()
|
|
444
|
+
result = client.add_synced_account(account_id, synced_account_id)
|
|
445
|
+
|
|
446
|
+
if output == "json":
|
|
447
|
+
console.print_json(json.dumps(result, default=str))
|
|
448
|
+
else:
|
|
449
|
+
name = result.get("AccountName", str(synced_account_id))
|
|
450
|
+
print_success(f"Synced account '{name}' (ID: {synced_account_id}) added to account ID: {account_id}")
|
|
451
|
+
|
|
452
|
+
except httpx.HTTPStatusError as e:
|
|
453
|
+
print_api_error(e, "synced-add")
|
|
454
|
+
raise typer.Exit(1)
|
|
455
|
+
except httpx.RequestError as e:
|
|
456
|
+
print_api_error(e, "synced-add")
|
|
457
|
+
raise typer.Exit(1)
|
|
458
|
+
except Exception as e:
|
|
459
|
+
print_api_error(e, "synced-add")
|
|
460
|
+
raise typer.Exit(1)
|
|
461
|
+
|
|
462
|
+
|
|
463
|
+
@app.command("synced-remove")
|
|
464
|
+
def synced_remove(
|
|
465
|
+
account_id: int = typer.Argument(..., help="Parent managed account ID"),
|
|
466
|
+
synced_account_id: int = typer.Argument(..., help="Synced account ID to remove"),
|
|
467
|
+
force: bool = typer.Option(False, "--force", "-f", help="Skip confirmation"),
|
|
468
|
+
) -> None:
|
|
469
|
+
"""Remove a specific synced account.
|
|
470
|
+
|
|
471
|
+
Examples:
|
|
472
|
+
bt pws accounts synced-remove 14 25
|
|
473
|
+
bt pws accounts synced-remove 14 25 --force
|
|
474
|
+
"""
|
|
475
|
+
try:
|
|
476
|
+
with get_client() as client:
|
|
477
|
+
client.authenticate()
|
|
478
|
+
|
|
479
|
+
if not force:
|
|
480
|
+
confirm = typer.confirm(f"Remove synced account {synced_account_id} from account {account_id}?")
|
|
481
|
+
if not confirm:
|
|
482
|
+
console.print("[yellow]Cancelled.[/yellow]")
|
|
483
|
+
raise typer.Exit(0)
|
|
484
|
+
|
|
485
|
+
client.remove_synced_account(account_id, synced_account_id)
|
|
486
|
+
|
|
487
|
+
print_success(f"Removed synced account ID: {synced_account_id} from account ID: {account_id}")
|
|
488
|
+
|
|
489
|
+
except httpx.HTTPStatusError as e:
|
|
490
|
+
print_api_error(e, "synced-remove")
|
|
491
|
+
raise typer.Exit(1)
|
|
492
|
+
except httpx.RequestError as e:
|
|
493
|
+
print_api_error(e, "synced-remove")
|
|
494
|
+
raise typer.Exit(1)
|
|
495
|
+
except typer.Exit:
|
|
496
|
+
raise
|
|
497
|
+
except Exception as e:
|
|
498
|
+
print_api_error(e, "synced-remove")
|
|
499
|
+
raise typer.Exit(1)
|
|
500
|
+
|
|
501
|
+
|
|
502
|
+
@app.command("synced-clear")
|
|
503
|
+
def synced_clear(
|
|
504
|
+
account_id: int = typer.Argument(..., help="Parent managed account ID"),
|
|
505
|
+
force: bool = typer.Option(False, "--force", "-f", help="Skip confirmation"),
|
|
506
|
+
) -> None:
|
|
507
|
+
"""Remove all synced accounts from a managed account.
|
|
508
|
+
|
|
509
|
+
Examples:
|
|
510
|
+
bt pws accounts synced-clear 14
|
|
511
|
+
bt pws accounts synced-clear 14 --force
|
|
512
|
+
"""
|
|
513
|
+
try:
|
|
514
|
+
with get_client() as client:
|
|
515
|
+
client.authenticate()
|
|
516
|
+
|
|
517
|
+
if not force:
|
|
518
|
+
confirm = typer.confirm(f"Remove ALL synced accounts from account {account_id}?")
|
|
519
|
+
if not confirm:
|
|
520
|
+
console.print("[yellow]Cancelled.[/yellow]")
|
|
521
|
+
raise typer.Exit(0)
|
|
522
|
+
|
|
523
|
+
client.remove_all_synced_accounts(account_id)
|
|
524
|
+
|
|
525
|
+
print_success(f"All synced accounts removed from account ID: {account_id}")
|
|
526
|
+
|
|
527
|
+
except httpx.HTTPStatusError as e:
|
|
528
|
+
print_api_error(e, "synced-clear")
|
|
529
|
+
raise typer.Exit(1)
|
|
530
|
+
except httpx.RequestError as e:
|
|
531
|
+
print_api_error(e, "synced-clear")
|
|
532
|
+
raise typer.Exit(1)
|
|
533
|
+
except typer.Exit:
|
|
534
|
+
raise
|
|
535
|
+
except Exception as e:
|
|
536
|
+
print_api_error(e, "synced-clear")
|
|
537
|
+
raise typer.Exit(1)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
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
|