bt-cli 0.4.71__tar.gz → 0.4.72__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.71 → bt_cli-0.4.72}/CLAUDE.md +2 -2
- {bt_cli-0.4.71 → bt_cli-0.4.72}/PKG-INFO +1 -1
- {bt_cli-0.4.71 → bt_cli-0.4.72}/pyproject.toml +1 -1
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/__init__.py +1 -1
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/commands/learn.py +1 -1
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/commands/quick.py +31 -31
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/core/output.py +3 -1
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/core/prompts.py +5 -5
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/entitle/commands/bundles.py +1 -1
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/entitle/commands/permissions.py +1 -1
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/entitle/commands/requests.py +2 -2
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/entitle/commands/resources.py +2 -2
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/epml/commands/rbp_cmdgrps.py +1 -1
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/epml/commands/rbp_hostgrps.py +1 -1
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/epml/commands/rbp_tmdategrps.py +1 -1
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/epml/commands/rbp_usergrps.py +1 -1
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/epmw/commands/policies.py +3 -3
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/epmw/commands/quick.py +4 -4
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/pra/commands/group_policies.py +1 -1
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/pra/commands/jump_clients.py +1 -1
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/pra/commands/jump_items.py +4 -4
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/pra/commands/quick.py +16 -16
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/pra/commands/users.py +2 -2
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/pra/commands/vault.py +3 -3
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/pws/commands/accounts.py +3 -3
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/pws/commands/assets.py +1 -1
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/pws/commands/attributes.py +2 -2
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/pws/commands/clouds.py +2 -2
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/pws/commands/databases.py +1 -1
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/pws/commands/functional.py +4 -4
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/pws/commands/quick.py +65 -65
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/pws/commands/search.py +2 -2
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/pws/commands/secrets.py +10 -10
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/pws/commands/sessions.py +3 -3
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/pws/commands/systems.py +4 -4
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/pws/commands/workgroups.py +1 -1
- bt_cli-0.4.72/tests/core/test_json_stdout_purity.py +304 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/tests/pws/test_commands.py +24 -13
- {bt_cli-0.4.71 → bt_cli-0.4.72}/.claude/skills/bt/SKILL.md +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/.claude/skills/entitle/SKILL.md +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/.claude/skills/epml/SKILL.md +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/.claude/skills/epmw/SKILL.md +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/.claude/skills/pra/SKILL.md +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/.claude/skills/pws/SKILL.md +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/.env.example +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/.github/workflows/ci.yml +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/.github/workflows/release.yml +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/.gitignore +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/README.md +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/assets/cli-help.png +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/assets/cli-output.png +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/bt-cli-cheatsheet.html +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/bt-cli.spec +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/bt_entry.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/epml-clients-server-side-filters-plan.md +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/epml-implementation-plan.md +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/pf-implementation-plan.md +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/scripts/bt_entry.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/scripts/pf_onboard.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/scripts/sync-package-data.sh +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/cli.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/commands/__init__.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/commands/configure.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/core/__init__.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/core/auth.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/core/client.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/core/config.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/core/config_file.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/core/csv_utils.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/core/errors.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/core/rest_debug.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/data/CLAUDE.md +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/data/__init__.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/data/skills/bt/SKILL.md +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/data/skills/entitle/SKILL.md +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/data/skills/epml/SKILL.md +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/data/skills/epmw/SKILL.md +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/data/skills/pf/SKILL.md +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/data/skills/pra/SKILL.md +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/data/skills/pws/SKILL.md +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/data/skills/secrets/SKILL.md +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/data/skills/session-analysis/SKILL.md +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/entitle/__init__.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/entitle/client/__init__.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/entitle/client/base.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/entitle/commands/__init__.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/entitle/commands/accounts.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/entitle/commands/applications.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/entitle/commands/audit.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/entitle/commands/auth.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/entitle/commands/integrations.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/entitle/commands/jit.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/entitle/commands/policies.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/entitle/commands/roles.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/entitle/commands/users.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/entitle/commands/workflows.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/entitle/models/__init__.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/entitle/models/bundle.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/entitle/models/common.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/entitle/models/integration.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/entitle/models/permission.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/entitle/models/policy.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/entitle/models/resource.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/entitle/models/role.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/entitle/models/user.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/entitle/models/workflow.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/epml/__init__.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/epml/client/__init__.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/epml/client/base.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/epml/commands/__init__.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/epml/commands/audit.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/epml/commands/auth.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/epml/commands/client_pkg.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/epml/commands/clients.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/epml/commands/external_apis.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/epml/commands/hosts.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/epml/commands/iolog.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/epml/commands/license.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/epml/commands/quick.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/epml/commands/rbp_entitlement.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/epml/commands/rbp_policy.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/epml/commands/rbp_roles.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/epml/commands/rbp_tests.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/epml/commands/rbp_tx.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/epml/commands/settings.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/epml/commands/siems.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/epml/commands/users.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/epml/models/__init__.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/epmw/__init__.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/epmw/client/__init__.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/epmw/client/base.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/epmw/commands/__init__.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/epmw/commands/audits.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/epmw/commands/auth.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/epmw/commands/computers.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/epmw/commands/events.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/epmw/commands/groups.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/epmw/commands/requests.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/epmw/commands/roles.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/epmw/commands/tasks.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/epmw/commands/users.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/epmw/models/__init__.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/pf/__init__.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/pf/client/__init__.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/pf/client/base.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/pf/commands/__init__.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/pf/commands/auth.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/pf/commands/machines.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/pf/commands/tokens.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/pf/commands/user.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/pf/commands/users.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/pra/__init__.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/pra/client/__init__.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/pra/client/base.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/pra/commands/__init__.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/pra/commands/auth.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/pra/commands/import_export.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/pra/commands/jump_groups.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/pra/commands/jumpoints.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/pra/commands/policies.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/pra/commands/teams.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/pra/models/__init__.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/pra/models/common.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/pra/models/group_policy.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/pra/models/jump_client.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/pra/models/jump_group.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/pra/models/jump_item.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/pra/models/jumpoint.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/pra/models/team.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/pra/models/user.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/pra/models/vault.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/pws/__init__.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/pws/client/__init__.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/pws/client/base.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/pws/client/beyondinsight.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/pws/client/passwordsafe.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/pws/commands/__init__.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/pws/commands/audit.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/pws/commands/auth.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/pws/commands/config.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/pws/commands/credentials.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/pws/commands/directories.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/pws/commands/import_export.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/pws/commands/platforms.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/pws/commands/users.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/pws/config.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/pws/models/__init__.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/pws/models/account.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/pws/models/asset.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/pws/models/common.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/pws/models/system.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/secrets/__init__.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/secrets/client/__init__.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/secrets/client/base.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/secrets/commands/__init__.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/secrets/commands/_hints.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/secrets/commands/auth.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/secrets/commands/dynamic.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/secrets/commands/folders.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/secrets/commands/integrations.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/secrets/commands/leases.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/secrets/commands/static.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/src/bt_cli/secrets/models/__init__.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/tests/__init__.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/tests/conftest.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/tests/core/__init__.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/tests/core/test_auth.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/tests/core/test_config.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/tests/core/test_config_file.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/tests/core/test_errors.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/tests/core/test_output.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/tests/core/test_rest_debug.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/tests/entitle/__init__.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/tests/entitle/test_client.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/tests/entitle/test_commands.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/tests/entitle-smoke-test.sh +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/tests/epml/__init__.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/tests/epml/test_client.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/tests/epml/test_commands.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/tests/epmw/__init__.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/tests/epmw/test_client.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/tests/epmw/test_commands.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/tests/epmw-quick-test-plan.md +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/tests/fixtures/__init__.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/tests/fixtures/responses.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/tests/integration/__init__.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/tests/integration/conftest.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/tests/integration/helpers.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/tests/integration/test_entitle_integration.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/tests/integration/test_epmw_integration.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/tests/integration/test_epmw_lifecycle.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/tests/integration/test_pra_integration.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/tests/integration/test_pra_lifecycle.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/tests/integration/test_pws_integration.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/tests/integration/test_pws_lifecycle.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/tests/pf/__init__.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/tests/pf/test_client.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/tests/pf/test_commands.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/tests/pra/__init__.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/tests/pra/test_client.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/tests/pra/test_commands.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/tests/pra/test_jump_item_fixes.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/tests/pra-smoke-test.sh +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/tests/pra-test-plan.md +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/tests/pws/__init__.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/tests/pws/test_client.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/tests/pws/test_quick_sessions.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/tests/pws-quick-test-plan.md +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/tests/pws-smoke-test.sh +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/tests/secrets/__init__.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/tests/secrets/test_client.py +0 -0
- {bt_cli-0.4.71 → bt_cli-0.4.72}/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.72**
|
|
4
4
|
|
|
5
5
|
## Setup
|
|
6
6
|
|
|
@@ -82,7 +82,7 @@ PASSWORD=$(bt pws quick checkout -s server -a admin --raw)
|
|
|
82
82
|
- PRA K8s tunnels: Require Linux jumpoint
|
|
83
83
|
- PWS assets: Must create via workgroup endpoint
|
|
84
84
|
- ECM integration: PWS system name must match PRA jump item name
|
|
85
|
-
-
|
|
85
|
+
- **`-o json` is pure JSON on stdout — in every product.** All human status/progress banners *and interactive prompts* go to **stderr** (`core/output.py` `err_console`; `typer.confirm`/`typer.prompt` are called with `err=True`), so `... -o json | jq` and `json.load(stdin)` are safe even for `quick` workflow commands that narrate their steps. Enforced by static guards in `tests/core/test_json_stdout_purity.py` — when adding a command, use `print_success`/`print_warning`/`print_info` or `err_console.print` for anything that isn't the command's data. Consequence: piping *table* output to a file drops the narration (it's on stderr) — that's intended. PRA API 4xx `errors` objects are surfaced field-by-field, not collapsed to "Validation Failed".
|
|
86
86
|
- **PRA tunnel create gates fields by `tunnel_type`**: tcp→`--ports`, mssql→`--username`/`--database`, k8s→`--url`/`--ca-cert`. **psql/mysql are created BARE** — the public Config API has no write path for their username/database (POST rejects, PATCH no-ops, PUT 405s) though GET shows them; CLI warns + ignores those flags. Set in `/login` UI.
|
|
87
87
|
- **PRA `export jump-items` defaults to a REAL export** (all types: shell/rdp/vnc/web/tunnel, full type-specific columns) that round-trips through `import jump-items`; `--sample` gives the blank template. `bt pra jump-items web create` exists.
|
|
88
88
|
- **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).
|
|
@@ -221,7 +221,7 @@ def clear_learnings(
|
|
|
221
221
|
bt learn clear --force
|
|
222
222
|
"""
|
|
223
223
|
if not force:
|
|
224
|
-
if not typer.confirm("Clear all learnings?"):
|
|
224
|
+
if not typer.confirm("Clear all learnings?", err=True):
|
|
225
225
|
console.print("[yellow]Cancelled[/yellow]")
|
|
226
226
|
raise typer.Exit(0)
|
|
227
227
|
|
|
@@ -11,7 +11,7 @@ from rich.console import Console
|
|
|
11
11
|
from rich.panel import Panel
|
|
12
12
|
from rich.table import Table
|
|
13
13
|
|
|
14
|
-
from ..core.output import print_api_error, print_error, print_success, print_warning
|
|
14
|
+
from ..core.output import err_console, print_api_error, print_error, print_success, print_warning
|
|
15
15
|
from ..core.prompts import prompt_if_missing, prompt_from_list
|
|
16
16
|
|
|
17
17
|
|
|
@@ -128,7 +128,7 @@ def pasm_onboard(
|
|
|
128
128
|
for i, row in enumerate(rows, 1):
|
|
129
129
|
console.print(f" {i}. {row.get('name', '?')} ({row.get('ip', '?')})")
|
|
130
130
|
|
|
131
|
-
if not typer.confirm("\nProceed with bulk onboarding?"):
|
|
131
|
+
if not typer.confirm("\nProceed with bulk onboarding?", err=True):
|
|
132
132
|
console.print("[yellow]Cancelled.[/yellow]")
|
|
133
133
|
raise typer.Exit(0)
|
|
134
134
|
|
|
@@ -158,19 +158,19 @@ def pasm_onboard(
|
|
|
158
158
|
)
|
|
159
159
|
results.append({"name": row.get('name'), "status": "success"})
|
|
160
160
|
except Exception as e:
|
|
161
|
-
|
|
161
|
+
err_console.print(f"[red]Failed: {e}[/red]")
|
|
162
162
|
results.append({"name": row.get('name'), "status": "failed", "error": str(e)})
|
|
163
163
|
|
|
164
164
|
# Summary
|
|
165
165
|
console.print(f"\n[bold]Bulk Onboarding Summary:[/bold]")
|
|
166
166
|
success = sum(1 for r in results if r["status"] == "success")
|
|
167
167
|
failed = sum(1 for r in results if r["status"] == "failed")
|
|
168
|
-
|
|
168
|
+
err_console.print(f" [green]Success: {success}[/green]")
|
|
169
169
|
if failed:
|
|
170
|
-
|
|
170
|
+
err_console.print(f" [red]Failed: {failed}[/red]")
|
|
171
171
|
for r in results:
|
|
172
172
|
if r["status"] == "failed":
|
|
173
|
-
|
|
173
|
+
err_console.print(f" - {r['name']}: {r.get('error', 'Unknown error')}")
|
|
174
174
|
|
|
175
175
|
if output == "json":
|
|
176
176
|
console.print_json(json.dumps(results, default=str))
|
|
@@ -273,7 +273,7 @@ def _onboard_single_host(
|
|
|
273
273
|
pws.authenticate()
|
|
274
274
|
|
|
275
275
|
# Create asset
|
|
276
|
-
|
|
276
|
+
err_console.print(f"[dim]PWS: Creating asset '{name}'...[/dim]")
|
|
277
277
|
asset = pws.create_asset(
|
|
278
278
|
workgroup_id=workgroup,
|
|
279
279
|
ip_address=ip,
|
|
@@ -281,10 +281,10 @@ def _onboard_single_host(
|
|
|
281
281
|
dns_name=dns,
|
|
282
282
|
)
|
|
283
283
|
asset_id = asset.get("AssetID")
|
|
284
|
-
|
|
284
|
+
err_console.print(f" [green]Created asset ID: {asset_id}[/green]")
|
|
285
285
|
|
|
286
286
|
# Create managed system
|
|
287
|
-
|
|
287
|
+
err_console.print(f"[dim]PWS: Creating managed system '{name}'...[/dim]")
|
|
288
288
|
system = pws.create_managed_system(
|
|
289
289
|
system_name=name,
|
|
290
290
|
platform_id=platform,
|
|
@@ -295,7 +295,7 @@ def _onboard_single_host(
|
|
|
295
295
|
elevation_command=elevation,
|
|
296
296
|
)
|
|
297
297
|
system_id = system.get("ManagedSystemID")
|
|
298
|
-
|
|
298
|
+
err_console.print(f" [green]Created managed system ID: {system_id}[/green]")
|
|
299
299
|
|
|
300
300
|
result["pws"] = {
|
|
301
301
|
"created": True,
|
|
@@ -314,7 +314,7 @@ def _onboard_single_host(
|
|
|
314
314
|
account_password = generate_password()
|
|
315
315
|
generated_password = True
|
|
316
316
|
|
|
317
|
-
|
|
317
|
+
err_console.print(f"[dim]PWS: Creating managed account '{account}'...[/dim]")
|
|
318
318
|
account_obj = pws.create_managed_account(
|
|
319
319
|
system_id=system_id,
|
|
320
320
|
account_name=account,
|
|
@@ -322,7 +322,7 @@ def _onboard_single_host(
|
|
|
322
322
|
auto_management_flag=True if functional_account else False,
|
|
323
323
|
)
|
|
324
324
|
account_id = account_obj.get("ManagedAccountID")
|
|
325
|
-
|
|
325
|
+
err_console.print(f" [green]Created managed account ID: {account_id}[/green]")
|
|
326
326
|
|
|
327
327
|
if generated_password:
|
|
328
328
|
console.print(f" [yellow]Generated password (save this!):[/yellow] [bold]{account_password}[/bold]")
|
|
@@ -339,7 +339,7 @@ def _onboard_single_host(
|
|
|
339
339
|
pra_user = pra_username or ("ec2-admin" if jump_type == "shell" else None)
|
|
340
340
|
|
|
341
341
|
if jump_type == "shell":
|
|
342
|
-
|
|
342
|
+
err_console.print(f"[dim]PRA: Creating shell jump item '{name}'...[/dim]")
|
|
343
343
|
jump_item = pra.create_shell_jump(
|
|
344
344
|
name=name,
|
|
345
345
|
hostname=ip,
|
|
@@ -350,9 +350,9 @@ def _onboard_single_host(
|
|
|
350
350
|
username=pra_user,
|
|
351
351
|
)
|
|
352
352
|
jump_id = jump_item.get("id")
|
|
353
|
-
|
|
353
|
+
err_console.print(f" [green]Created shell jump ID: {jump_id}[/green]")
|
|
354
354
|
else:
|
|
355
|
-
|
|
355
|
+
err_console.print(f"[dim]PRA: Creating RDP jump item '{name}'...[/dim]")
|
|
356
356
|
jump_item = pra.create_rdp_jump(
|
|
357
357
|
name=name,
|
|
358
358
|
hostname=ip,
|
|
@@ -361,7 +361,7 @@ def _onboard_single_host(
|
|
|
361
361
|
rdp_port=port if port != 22 else 3389,
|
|
362
362
|
)
|
|
363
363
|
jump_id = jump_item.get("id")
|
|
364
|
-
|
|
364
|
+
err_console.print(f" [green]Created RDP jump ID: {jump_id}[/green]")
|
|
365
365
|
|
|
366
366
|
result["pra"] = {
|
|
367
367
|
"created": True,
|
|
@@ -489,7 +489,7 @@ def pasm_offboard(
|
|
|
489
489
|
else:
|
|
490
490
|
pws_asset_id = pws_system.get("AssetID")
|
|
491
491
|
except Exception as e:
|
|
492
|
-
|
|
492
|
+
err_console.print(f"[yellow]PWS search error: {e}[/yellow]")
|
|
493
493
|
|
|
494
494
|
# Search PRA
|
|
495
495
|
if not skip_pra:
|
|
@@ -512,7 +512,7 @@ def pasm_offboard(
|
|
|
512
512
|
pra_jump_type = "rdp"
|
|
513
513
|
break
|
|
514
514
|
except Exception as e:
|
|
515
|
-
|
|
515
|
+
err_console.print(f"[yellow]PRA search error: {e}[/yellow]")
|
|
516
516
|
|
|
517
517
|
# Show what will be deleted
|
|
518
518
|
console.print(f"\n[bold]Will delete '{name}':[/bold]")
|
|
@@ -541,7 +541,7 @@ def pasm_offboard(
|
|
|
541
541
|
|
|
542
542
|
# Confirm
|
|
543
543
|
if not force:
|
|
544
|
-
confirm = typer.confirm("\nProceed with deletion?")
|
|
544
|
+
confirm = typer.confirm("\nProceed with deletion?", err=True)
|
|
545
545
|
if not confirm:
|
|
546
546
|
console.print("[yellow]Cancelled.[/yellow]")
|
|
547
547
|
raise typer.Exit(0)
|
|
@@ -557,21 +557,21 @@ def pasm_offboard(
|
|
|
557
557
|
for acc in pws_accounts:
|
|
558
558
|
acc_id = acc.get("ManagedAccountID", acc.get("AccountId"))
|
|
559
559
|
acc_name = acc.get("AccountName")
|
|
560
|
-
|
|
560
|
+
err_console.print(f"[dim]PWS: Deleting account {acc_name}...[/dim]")
|
|
561
561
|
pws.delete_managed_account(acc_id)
|
|
562
562
|
deleted_accounts.append({"id": acc_id, "name": acc_name})
|
|
563
|
-
|
|
563
|
+
err_console.print(f" [green]Deleted account: {acc_name}[/green]")
|
|
564
564
|
|
|
565
565
|
# Delete system
|
|
566
|
-
|
|
566
|
+
err_console.print(f"[dim]PWS: Deleting system {pws_system.get('SystemName')}...[/dim]")
|
|
567
567
|
pws.delete_managed_system(system_id)
|
|
568
|
-
|
|
568
|
+
err_console.print(f" [green]Deleted system: {pws_system.get('SystemName')}[/green]")
|
|
569
569
|
|
|
570
570
|
# Delete asset
|
|
571
571
|
if pws_asset_id:
|
|
572
|
-
|
|
572
|
+
err_console.print(f"[dim]PWS: Deleting asset {pws_asset_id}...[/dim]")
|
|
573
573
|
pws.delete_asset(pws_asset_id)
|
|
574
|
-
|
|
574
|
+
err_console.print(f" [green]Deleted asset ID: {pws_asset_id}[/green]")
|
|
575
575
|
|
|
576
576
|
result["pws"] = {
|
|
577
577
|
"deleted": True,
|
|
@@ -585,12 +585,12 @@ def pasm_offboard(
|
|
|
585
585
|
pra = get_pra_client()
|
|
586
586
|
jump_id = pra_jump.get("id")
|
|
587
587
|
|
|
588
|
-
|
|
588
|
+
err_console.print(f"[dim]PRA: Deleting {pra_jump_type} jump item {pra_jump.get('name')}...[/dim]")
|
|
589
589
|
if pra_jump_type == "shell":
|
|
590
590
|
pra.delete_shell_jump(jump_id)
|
|
591
591
|
else:
|
|
592
592
|
pra.delete_rdp_jump(jump_id)
|
|
593
|
-
|
|
593
|
+
err_console.print(f" [green]Deleted {pra_jump_type} jump: {pra_jump.get('name')}[/green]")
|
|
594
594
|
|
|
595
595
|
result["pra"] = {
|
|
596
596
|
"deleted": True,
|
|
@@ -650,7 +650,7 @@ def pasm_search(
|
|
|
650
650
|
try:
|
|
651
651
|
with get_pws_client() as pws:
|
|
652
652
|
pws.authenticate()
|
|
653
|
-
|
|
653
|
+
err_console.print(f"[dim]Searching PWS for '{query}'...[/dim]")
|
|
654
654
|
|
|
655
655
|
systems = pws.list_managed_systems()
|
|
656
656
|
result["pws"]["systems"] = [
|
|
@@ -661,12 +661,12 @@ def pasm_search(
|
|
|
661
661
|
accounts = pws.list_managed_accounts(account_name=query)
|
|
662
662
|
result["pws"]["accounts"] = accounts[:20]
|
|
663
663
|
except Exception as e:
|
|
664
|
-
|
|
664
|
+
err_console.print(f"[yellow]PWS search error: {e}[/yellow]")
|
|
665
665
|
|
|
666
666
|
# Search PRA
|
|
667
667
|
try:
|
|
668
668
|
pra = get_pra_client()
|
|
669
|
-
|
|
669
|
+
err_console.print(f"[dim]Searching PRA for '{query}'...[/dim]")
|
|
670
670
|
|
|
671
671
|
shell_jumps = pra.list_shell_jumps()
|
|
672
672
|
result["pra"]["shell_jumps"] = [
|
|
@@ -682,7 +682,7 @@ def pasm_search(
|
|
|
682
682
|
or query_lower in j.get("hostname", "").lower()
|
|
683
683
|
][:20]
|
|
684
684
|
except Exception as e:
|
|
685
|
-
|
|
685
|
+
err_console.print(f"[yellow]PRA search error: {e}[/yellow]")
|
|
686
686
|
|
|
687
687
|
if output == "json":
|
|
688
688
|
console.print_json(json.dumps(result, default=str))
|
|
@@ -195,7 +195,9 @@ def confirm_action(message: str, default: bool = False) -> bool:
|
|
|
195
195
|
True if confirmed, False otherwise
|
|
196
196
|
"""
|
|
197
197
|
suffix = " [Y/n]" if default else " [y/N]"
|
|
198
|
-
|
|
198
|
+
# Prompt on stderr (input is still read from stdin) so a confirmation in a
|
|
199
|
+
# `-o json` flow doesn't land in the JSON document on stdout.
|
|
200
|
+
response = err_console.input(f"{message}{suffix} ").strip().lower()
|
|
199
201
|
|
|
200
202
|
if not response:
|
|
201
203
|
return default
|
|
@@ -35,7 +35,7 @@ def prompt_if_missing(
|
|
|
35
35
|
The value (either original or prompted)
|
|
36
36
|
"""
|
|
37
37
|
if value is None or value == "":
|
|
38
|
-
raw = typer.prompt(prompt_text, type=value_type)
|
|
38
|
+
raw = typer.prompt(prompt_text, type=value_type, err=True)
|
|
39
39
|
if value_type is str:
|
|
40
40
|
return _clean_str(raw) # type: ignore[return-value]
|
|
41
41
|
return raw
|
|
@@ -69,7 +69,7 @@ def prompt_from_list(
|
|
|
69
69
|
item_id = item.get(id_key, "")
|
|
70
70
|
item_name = item.get(name_key, "Unknown")
|
|
71
71
|
console.print(f" {str(item_id):>{id_width}}: {item_name}")
|
|
72
|
-
raw = typer.prompt(prompt_text, type=value_type)
|
|
72
|
+
raw = typer.prompt(prompt_text, type=value_type, err=True)
|
|
73
73
|
if value_type is str:
|
|
74
74
|
return _clean_str(raw) # type: ignore[return-value]
|
|
75
75
|
return raw
|
|
@@ -95,11 +95,11 @@ def prompt_choice(
|
|
|
95
95
|
marker = " (default)" if value == default else ""
|
|
96
96
|
console.print(f" {value}: {desc}{marker}")
|
|
97
97
|
|
|
98
|
-
result = typer.prompt("Choice", default=default or choices[0][0])
|
|
98
|
+
result = typer.prompt("Choice", default=default or choices[0][0], err=True)
|
|
99
99
|
valid_values = [v for v, _ in choices]
|
|
100
100
|
while result not in valid_values:
|
|
101
101
|
console.print(f"[red]Invalid choice. Options: {', '.join(valid_values)}[/red]")
|
|
102
|
-
result = typer.prompt("Choice", default=default or choices[0][0])
|
|
102
|
+
result = typer.prompt("Choice", default=default or choices[0][0], err=True)
|
|
103
103
|
return result
|
|
104
104
|
|
|
105
105
|
|
|
@@ -148,7 +148,7 @@ def prompt_filtered_pick(
|
|
|
148
148
|
"[dim]Type a number to pick, text to filter, n/p for next/prev page, blank to cancel[/dim]"
|
|
149
149
|
)
|
|
150
150
|
|
|
151
|
-
ans = typer.prompt(">", default="", show_default=False).strip()
|
|
151
|
+
ans = typer.prompt(">", default="", show_default=False, err=True).strip()
|
|
152
152
|
if ans == "":
|
|
153
153
|
raise typer.Abort()
|
|
154
154
|
low = ans.lower()
|
|
@@ -201,7 +201,7 @@ def delete_bundle(
|
|
|
201
201
|
) -> None:
|
|
202
202
|
"""Delete a bundle."""
|
|
203
203
|
if not confirm:
|
|
204
|
-
typer.confirm(f"Are you sure you want to delete bundle {bundle_id}?", abort=True)
|
|
204
|
+
typer.confirm(f"Are you sure you want to delete bundle {bundle_id}?", abort=True, err=True)
|
|
205
205
|
|
|
206
206
|
try:
|
|
207
207
|
with get_client() as client:
|
|
@@ -53,7 +53,7 @@ def revoke_permission(
|
|
|
53
53
|
) -> None:
|
|
54
54
|
"""Revoke a permission."""
|
|
55
55
|
if not confirm:
|
|
56
|
-
typer.confirm(f"Are you sure you want to revoke permission {permission_id}?", abort=True)
|
|
56
|
+
typer.confirm(f"Are you sure you want to revoke permission {permission_id}?", abort=True, err=True)
|
|
57
57
|
|
|
58
58
|
try:
|
|
59
59
|
with get_client() as client:
|
|
@@ -141,7 +141,7 @@ def _pick_duration_interactive(
|
|
|
141
141
|
|
|
142
142
|
def _prompt_justification() -> str:
|
|
143
143
|
while True:
|
|
144
|
-
text = typer.prompt("Justification (business reason)").strip()
|
|
144
|
+
text = typer.prompt("Justification (business reason)", err=True).strip()
|
|
145
145
|
if 1 <= len(text) <= 2048:
|
|
146
146
|
return text
|
|
147
147
|
console.print("[red]Justification must be 1..2048 characters[/red]")
|
|
@@ -252,7 +252,7 @@ def create_access_request(
|
|
|
252
252
|
)
|
|
253
253
|
|
|
254
254
|
if not yes:
|
|
255
|
-
typer.confirm("Submit this request?", default=True, abort=True)
|
|
255
|
+
typer.confirm("Submit this request?", default=True, abort=True, err=True)
|
|
256
256
|
|
|
257
257
|
with get_client(user_token=True) as writer:
|
|
258
258
|
result = writer.create_access_request(
|
|
@@ -6,7 +6,7 @@ import httpx
|
|
|
6
6
|
import typer
|
|
7
7
|
|
|
8
8
|
from ..client.base import get_client
|
|
9
|
-
from ...core.output import console, print_table, print_json, print_error, print_api_error
|
|
9
|
+
from ...core.output import err_console, console, print_table, print_json, print_error, print_api_error
|
|
10
10
|
|
|
11
11
|
app = typer.Typer(no_args_is_help=True, help="Manage resources")
|
|
12
12
|
|
|
@@ -98,7 +98,7 @@ def create_virtual_resource(
|
|
|
98
98
|
role_name=role_name,
|
|
99
99
|
)
|
|
100
100
|
resource = result.get("result", result)
|
|
101
|
-
|
|
101
|
+
err_console.print(f"Created virtual resource: {resource.get('name')} (ID: {resource.get('id')})")
|
|
102
102
|
print_json(result)
|
|
103
103
|
except httpx.HTTPStatusError as e:
|
|
104
104
|
print_api_error(e, "create virtual resource")
|
|
@@ -135,7 +135,7 @@ def clear_commands(
|
|
|
135
135
|
"""
|
|
136
136
|
from bt_cli.epml.client import get_client
|
|
137
137
|
if not yes:
|
|
138
|
-
typer.confirm(f"Clear all commands in cmdgrp {cmdgrp_id}?", abort=True)
|
|
138
|
+
typer.confirm(f"Clear all commands in cmdgrp {cmdgrp_id}?", abort=True, err=True)
|
|
139
139
|
try:
|
|
140
140
|
with get_client() as c:
|
|
141
141
|
c.clear_cmdgrp_commands(cmdgrp_id, host_id=host)
|
|
@@ -139,7 +139,7 @@ def clear_hosts(
|
|
|
139
139
|
"""
|
|
140
140
|
from bt_cli.epml.client import get_client
|
|
141
141
|
if not yes:
|
|
142
|
-
typer.confirm(f"Clear all hosts in hostgrp {hostgrp_id}?", abort=True)
|
|
142
|
+
typer.confirm(f"Clear all hosts in hostgrp {hostgrp_id}?", abort=True, err=True)
|
|
143
143
|
try:
|
|
144
144
|
with get_client() as c:
|
|
145
145
|
c.clear_hostgrp_hosts(hostgrp_id, host_id=host)
|
|
@@ -218,7 +218,7 @@ def clear_tmdates(
|
|
|
218
218
|
"""
|
|
219
219
|
from bt_cli.epml.client import get_client
|
|
220
220
|
if not yes:
|
|
221
|
-
typer.confirm(f"Clear all tmdates in tmdategrp {tmdategrp_id}?", abort=True)
|
|
221
|
+
typer.confirm(f"Clear all tmdates in tmdategrp {tmdategrp_id}?", abort=True, err=True)
|
|
222
222
|
try:
|
|
223
223
|
with get_client() as c:
|
|
224
224
|
c.clear_tmdategrp_tmdates(tmdategrp_id, host_id=host)
|
|
@@ -139,7 +139,7 @@ def clear_users(
|
|
|
139
139
|
"""
|
|
140
140
|
from bt_cli.epml.client import get_client
|
|
141
141
|
if not yes:
|
|
142
|
-
typer.confirm(f"Clear all users in usergrp {usergrp_id}?", abort=True)
|
|
142
|
+
typer.confirm(f"Clear all users in usergrp {usergrp_id}?", abort=True, err=True)
|
|
143
143
|
try:
|
|
144
144
|
with get_client() as c:
|
|
145
145
|
c.clear_usergrp_users(usergrp_id, host_id=host)
|
|
@@ -199,7 +199,7 @@ def delete_policy(
|
|
|
199
199
|
if not force:
|
|
200
200
|
policy = client.get_policy(policy_id)
|
|
201
201
|
policy_name = policy.get("name", policy_id)
|
|
202
|
-
if not typer.confirm(f"Delete policy '{policy_name}'?"):
|
|
202
|
+
if not typer.confirm(f"Delete policy '{policy_name}'?", err=True):
|
|
203
203
|
print_warning("Cancelled.")
|
|
204
204
|
raise typer.Exit(0)
|
|
205
205
|
|
|
@@ -266,7 +266,7 @@ def revert_policy(
|
|
|
266
266
|
if not policy.get("hasOpenDraft"):
|
|
267
267
|
print_warning(f"Policy '{policy_name}' has no draft to revert.")
|
|
268
268
|
raise typer.Exit(0)
|
|
269
|
-
if not typer.confirm(f"Revert draft changes for policy '{policy_name}'?"):
|
|
269
|
+
if not typer.confirm(f"Revert draft changes for policy '{policy_name}'?", err=True):
|
|
270
270
|
print_warning("Cancelled.")
|
|
271
271
|
raise typer.Exit(0)
|
|
272
272
|
|
|
@@ -668,7 +668,7 @@ def delete_app_group(
|
|
|
668
668
|
if not force:
|
|
669
669
|
group = client.get_application_group(policy_id, app_group_id)
|
|
670
670
|
group_name = group.get("name", app_group_id)
|
|
671
|
-
if not typer.confirm(f"Delete application group '{group_name}'?"):
|
|
671
|
+
if not typer.confirm(f"Delete application group '{group_name}'?", err=True):
|
|
672
672
|
print_warning("Cancelled.")
|
|
673
673
|
raise typer.Exit(0)
|
|
674
674
|
|
|
@@ -127,7 +127,7 @@ def stale_computers(
|
|
|
127
127
|
console.print()
|
|
128
128
|
if not force:
|
|
129
129
|
confirm = typer.confirm(
|
|
130
|
-
f"Delete {len(stale_computers)} stale computer(s)?"
|
|
130
|
+
f"Delete {len(stale_computers)} stale computer(s)?", err=True
|
|
131
131
|
)
|
|
132
132
|
if not confirm:
|
|
133
133
|
console.print("[yellow]Deletion cancelled.[/yellow]")
|
|
@@ -417,7 +417,7 @@ def move_computer(
|
|
|
417
417
|
)
|
|
418
418
|
console.print(table)
|
|
419
419
|
|
|
420
|
-
selection = typer.prompt("Select computer number", type=int)
|
|
420
|
+
selection = typer.prompt("Select computer number", type=int, err=True)
|
|
421
421
|
if selection < 1 or selection > len(computers):
|
|
422
422
|
print_error(f"Invalid selection. Choose 1-{len(computers)}")
|
|
423
423
|
raise typer.Exit(1)
|
|
@@ -469,7 +469,7 @@ def move_computer(
|
|
|
469
469
|
)
|
|
470
470
|
console.print(table)
|
|
471
471
|
|
|
472
|
-
selection = typer.prompt("Select target group number", type=int)
|
|
472
|
+
selection = typer.prompt("Select target group number", type=int, err=True)
|
|
473
473
|
if selection < 1 or selection > len(groups):
|
|
474
474
|
print_error(f"Invalid selection. Choose 1-{len(groups)}")
|
|
475
475
|
raise typer.Exit(1)
|
|
@@ -490,7 +490,7 @@ def move_computer(
|
|
|
490
490
|
console.print(f"[bold]From group:[/bold] {current_group}")
|
|
491
491
|
console.print(f"[bold]To group:[/bold] {group_name}")
|
|
492
492
|
console.print()
|
|
493
|
-
confirm = typer.confirm("Proceed with move?")
|
|
493
|
+
confirm = typer.confirm("Proceed with move?", err=True)
|
|
494
494
|
if not confirm:
|
|
495
495
|
console.print("[yellow]Cancelled.[/yellow]")
|
|
496
496
|
raise typer.Exit(0)
|
|
@@ -132,7 +132,7 @@ def delete_group_policy(
|
|
|
132
132
|
from rich.console import Console
|
|
133
133
|
|
|
134
134
|
if not yes:
|
|
135
|
-
if not typer.confirm(f"Delete Group Policy {policy_id}?"):
|
|
135
|
+
if not typer.confirm(f"Delete Group Policy {policy_id}?", err=True):
|
|
136
136
|
raise typer.Exit(0)
|
|
137
137
|
try:
|
|
138
138
|
get_client().delete_group_policy(policy_id)
|
|
@@ -122,7 +122,7 @@ def delete_jump_client(
|
|
|
122
122
|
from bt_cli.pra.client import get_client
|
|
123
123
|
|
|
124
124
|
if not force:
|
|
125
|
-
typer.confirm(f"Delete jump client {client_id}?", abort=True)
|
|
125
|
+
typer.confirm(f"Delete jump client {client_id}?", abort=True, err=True)
|
|
126
126
|
|
|
127
127
|
try:
|
|
128
128
|
client = get_client()
|
|
@@ -262,7 +262,7 @@ def delete_shell_jump(
|
|
|
262
262
|
return
|
|
263
263
|
|
|
264
264
|
if not force:
|
|
265
|
-
typer.confirm(f"Delete shell jump '{item.get('name')}' (ID: {item_id})?", abort=True)
|
|
265
|
+
typer.confirm(f"Delete shell jump '{item.get('name')}' (ID: {item_id})?", abort=True, err=True)
|
|
266
266
|
|
|
267
267
|
client.delete_shell_jump(item_id)
|
|
268
268
|
print_success(f"Deleted shell jump '{item.get('name')}' (ID: {item_id})")
|
|
@@ -458,7 +458,7 @@ def delete_rdp_jump(
|
|
|
458
458
|
return
|
|
459
459
|
|
|
460
460
|
if not force:
|
|
461
|
-
typer.confirm(f"Delete RDP jump '{item.get('name')}' (ID: {item_id})?", abort=True)
|
|
461
|
+
typer.confirm(f"Delete RDP jump '{item.get('name')}' (ID: {item_id})?", abort=True, err=True)
|
|
462
462
|
|
|
463
463
|
client.delete_rdp_jump(item_id)
|
|
464
464
|
print_success(f"Deleted RDP jump '{item.get('name')}' (ID: {item_id})")
|
|
@@ -705,7 +705,7 @@ def delete_web_jump(
|
|
|
705
705
|
return
|
|
706
706
|
|
|
707
707
|
if not force:
|
|
708
|
-
typer.confirm(f"Delete web jump '{item.get('name')}' (ID: {item_id})?", abort=True)
|
|
708
|
+
typer.confirm(f"Delete web jump '{item.get('name')}' (ID: {item_id})?", abort=True, err=True)
|
|
709
709
|
|
|
710
710
|
client.delete_web_jump(item_id)
|
|
711
711
|
print_success(f"Deleted web jump '{item.get('name')}' (ID: {item_id})")
|
|
@@ -941,7 +941,7 @@ def delete_protocol_tunnel(
|
|
|
941
941
|
return
|
|
942
942
|
|
|
943
943
|
if not force:
|
|
944
|
-
typer.confirm(f"Delete protocol tunnel '{item.get('name')}' (ID: {item_id})?", abort=True)
|
|
944
|
+
typer.confirm(f"Delete protocol tunnel '{item.get('name')}' (ID: {item_id})?", abort=True, err=True)
|
|
945
945
|
|
|
946
946
|
client.delete_protocol_tunnel(item_id)
|
|
947
947
|
print_success(f"Deleted protocol tunnel '{item.get('name')}' (ID: {item_id})")
|