bt-cli 0.4.49__tar.gz → 0.4.51__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.49 → bt_cli-0.4.51}/CLAUDE.md +2 -2
- {bt_cli-0.4.49 → bt_cli-0.4.51}/PKG-INFO +1 -1
- {bt_cli-0.4.49 → bt_cli-0.4.51}/pyproject.toml +1 -1
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/__init__.py +1 -1
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/core/config.py +36 -6
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/core/config_file.py +1 -1
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/core/errors.py +13 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/core/prompts.py +16 -2
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/data/skills/secrets/SKILL.md +9 -4
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/secrets/client/base.py +5 -3
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/secrets/commands/auth.py +1 -1
- {bt_cli-0.4.49 → bt_cli-0.4.51}/tests/secrets/test_client.py +28 -4
- {bt_cli-0.4.49 → bt_cli-0.4.51}/tests/secrets/test_commands.py +2 -2
- {bt_cli-0.4.49 → bt_cli-0.4.51}/.claude/skills/bt/SKILL.md +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/.claude/skills/entitle/SKILL.md +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/.claude/skills/epml/SKILL.md +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/.claude/skills/epmw/SKILL.md +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/.claude/skills/pra/SKILL.md +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/.claude/skills/pws/SKILL.md +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/.env.example +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/.github/workflows/ci.yml +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/.github/workflows/release.yml +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/.gitignore +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/README.md +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/assets/cli-help.png +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/assets/cli-output.png +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/bt-cli.spec +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/bt_entry.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/epml-clients-server-side-filters-plan.md +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/epml-implementation-plan.md +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/pf-implementation-plan.md +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/scripts/bt_entry.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/scripts/pf_onboard.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/scripts/sync-package-data.sh +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/cli.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/commands/__init__.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/commands/configure.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/commands/learn.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/commands/quick.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/core/__init__.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/core/auth.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/core/client.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/core/csv_utils.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/core/output.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/core/rest_debug.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/data/CLAUDE.md +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/data/__init__.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/data/skills/bt/SKILL.md +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/data/skills/entitle/SKILL.md +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/data/skills/epml/SKILL.md +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/data/skills/epmw/SKILL.md +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/data/skills/pra/SKILL.md +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/data/skills/pws/SKILL.md +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/entitle/__init__.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/entitle/client/__init__.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/entitle/client/base.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/entitle/commands/__init__.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/entitle/commands/accounts.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/entitle/commands/applications.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/entitle/commands/auth.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/entitle/commands/bundles.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/entitle/commands/integrations.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/entitle/commands/permissions.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/entitle/commands/policies.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/entitle/commands/requests.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/entitle/commands/resources.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/entitle/commands/roles.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/entitle/commands/users.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/entitle/commands/workflows.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/entitle/models/__init__.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/entitle/models/bundle.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/entitle/models/common.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/entitle/models/integration.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/entitle/models/permission.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/entitle/models/policy.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/entitle/models/resource.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/entitle/models/role.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/entitle/models/user.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/entitle/models/workflow.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/epml/__init__.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/epml/client/__init__.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/epml/client/base.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/epml/commands/__init__.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/epml/commands/audit.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/epml/commands/auth.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/epml/commands/client_pkg.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/epml/commands/clients.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/epml/commands/external_apis.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/epml/commands/hosts.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/epml/commands/iolog.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/epml/commands/license.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/epml/commands/quick.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/epml/commands/rbp_cmdgrps.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/epml/commands/rbp_entitlement.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/epml/commands/rbp_hostgrps.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/epml/commands/rbp_policy.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/epml/commands/rbp_roles.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/epml/commands/rbp_tests.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/epml/commands/rbp_tmdategrps.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/epml/commands/rbp_tx.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/epml/commands/rbp_usergrps.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/epml/commands/settings.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/epml/commands/siems.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/epml/commands/users.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/epml/models/__init__.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/epmw/__init__.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/epmw/client/__init__.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/epmw/client/base.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/epmw/commands/__init__.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/epmw/commands/audits.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/epmw/commands/auth.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/epmw/commands/computers.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/epmw/commands/events.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/epmw/commands/groups.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/epmw/commands/policies.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/epmw/commands/quick.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/epmw/commands/requests.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/epmw/commands/roles.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/epmw/commands/tasks.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/epmw/commands/users.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/epmw/models/__init__.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/pra/__init__.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/pra/client/__init__.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/pra/client/base.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/pra/commands/__init__.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/pra/commands/auth.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/pra/commands/group_policies.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/pra/commands/import_export.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/pra/commands/jump_clients.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/pra/commands/jump_groups.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/pra/commands/jump_items.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/pra/commands/jumpoints.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/pra/commands/policies.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/pra/commands/quick.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/pra/commands/teams.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/pra/commands/users.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/pra/commands/vault.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/pra/models/__init__.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/pra/models/common.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/pra/models/group_policy.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/pra/models/jump_client.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/pra/models/jump_group.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/pra/models/jump_item.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/pra/models/jumpoint.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/pra/models/team.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/pra/models/user.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/pra/models/vault.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/pws/__init__.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/pws/client/__init__.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/pws/client/base.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/pws/client/beyondinsight.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/pws/client/passwordsafe.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/pws/commands/__init__.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/pws/commands/accounts.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/pws/commands/assets.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/pws/commands/attributes.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/pws/commands/auth.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/pws/commands/clouds.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/pws/commands/config.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/pws/commands/credentials.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/pws/commands/databases.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/pws/commands/directories.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/pws/commands/functional.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/pws/commands/import_export.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/pws/commands/platforms.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/pws/commands/quick.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/pws/commands/search.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/pws/commands/secrets.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/pws/commands/systems.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/pws/commands/users.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/pws/commands/workgroups.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/pws/config.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/pws/models/__init__.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/pws/models/account.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/pws/models/asset.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/pws/models/common.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/pws/models/system.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/secrets/__init__.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/secrets/client/__init__.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/secrets/commands/__init__.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/secrets/commands/_hints.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/secrets/commands/dynamic.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/secrets/commands/folders.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/secrets/commands/integrations.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/secrets/commands/leases.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/secrets/commands/static.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/src/bt_cli/secrets/models/__init__.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/tests/__init__.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/tests/conftest.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/tests/core/__init__.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/tests/core/test_auth.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/tests/core/test_config.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/tests/core/test_errors.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/tests/core/test_rest_debug.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/tests/entitle/__init__.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/tests/entitle/test_client.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/tests/entitle/test_commands.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/tests/entitle-smoke-test.sh +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/tests/epml/__init__.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/tests/epml/test_client.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/tests/epml/test_commands.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/tests/epmw/__init__.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/tests/epmw/test_client.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/tests/epmw/test_commands.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/tests/epmw-quick-test-plan.md +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/tests/fixtures/__init__.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/tests/fixtures/responses.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/tests/integration/__init__.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/tests/integration/conftest.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/tests/integration/helpers.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/tests/integration/test_entitle_integration.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/tests/integration/test_epmw_integration.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/tests/integration/test_epmw_lifecycle.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/tests/integration/test_pra_integration.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/tests/integration/test_pra_lifecycle.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/tests/integration/test_pws_integration.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/tests/integration/test_pws_lifecycle.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/tests/pra/__init__.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/tests/pra/test_client.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/tests/pra/test_commands.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/tests/pra-smoke-test.sh +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/tests/pra-test-plan.md +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/tests/pws/__init__.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/tests/pws/test_client.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/tests/pws/test_commands.py +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/tests/pws-quick-test-plan.md +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/tests/pws-smoke-test.sh +0 -0
- {bt_cli-0.4.49 → bt_cli-0.4.51}/tests/secrets/__init__.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.51**
|
|
4
4
|
|
|
5
5
|
## Setup
|
|
6
6
|
|
|
@@ -87,7 +87,7 @@ PASSWORD=$(bt pws quick checkout -s server -a admin --raw)
|
|
|
87
87
|
- **EPML body-shape gotchas**: role create needs `action ∈ {A,R}`; hostgrp/usergrp create needs `type ∈ {I,E}` (Internal=static / External=directory-resolved); role assignments need `--kind S|R|B` for hostgrps/usergrps (S=Submit user, R=Run-as user); collection deletes use `?id=N` query params (CLI loops); child collections (commands/hosts/users/tmdates) have **no per-item delete** — `clear` nukes all, use `replace` to keep some.
|
|
88
88
|
- **EPML role update is upsert-on-id (overwrite, not partial)**: hitting `POST /roles` with `{id, ...}` clears any field you don't include. The CLI's `roles update` does read-modify-write so it feels partial; child relations are filtered out of the merge so assignments survive. Direct `curl` users must send the full record.
|
|
89
89
|
- **EPML role banner format**: `bt epml rbp roles create --banner-text "Title"` builds a `############`-framed message using `%rbprole%`/`%event%` server substitutions, mirroring the appliance's existing roles. See the worked role example in the EPM-L SKILL.md.
|
|
90
|
-
- **Secrets dynamic path is `/dynamic`** (NOT `/dynamic-secrets` as the doc claims); version header is `2026-
|
|
90
|
+
- **Secrets dynamic path is `/dynamic`** (NOT `/dynamic-secrets` as the doc claims); version header is `2026-04-28` (NOT 2026-01-02; server also rejects every earlier dated header — bumps are mandatory, not optional); `dynamic/{name}/generate` returns 201; `/leases/revoke` is scope-gated and 403 is the expected lab response — `bt secrets leases revoke` exits 3 with a clean warning, not generic failure. Documented in `src/bt_cli/data/skills/secrets/SKILL.md`.
|
|
91
91
|
- **Secrets `generate` mints real AWS STS credentials and is audited** — don't loop it in dev. Use `dynamic list`/`get` + `leases list` for browsing.
|
|
92
92
|
|
|
93
93
|
## Functional vs Managed Accounts
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"""Multi-product configuration management for BeyondTrust CLI."""
|
|
2
2
|
|
|
3
3
|
import os
|
|
4
|
+
import re
|
|
4
5
|
from dataclasses import dataclass, field
|
|
5
6
|
from typing import Any, Optional
|
|
6
7
|
|
|
@@ -133,7 +134,7 @@ class SecretsConfig(ProductConfig):
|
|
|
133
134
|
|
|
134
135
|
site_id: str = ""
|
|
135
136
|
pat: str = ""
|
|
136
|
-
api_version: str = "2026-
|
|
137
|
+
api_version: str = "2026-04-28"
|
|
137
138
|
|
|
138
139
|
def validate(self) -> None:
|
|
139
140
|
"""Validate configuration."""
|
|
@@ -222,6 +223,31 @@ def _resolve_value(value: Any) -> Any:
|
|
|
222
223
|
return value
|
|
223
224
|
|
|
224
225
|
|
|
226
|
+
_ANSI_CSI = re.compile(r"\x1b\[[0-?]*[ -/]*[@-~]")
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
def _strip_controls(value: Any) -> Any:
|
|
230
|
+
"""Remove ANSI escape sequences and bare control bytes from a config string.
|
|
231
|
+
|
|
232
|
+
Why: interactive prompts and copy-paste from terminals occasionally
|
|
233
|
+
capture ANSI escape sequences (cursor-arrow keystrokes are the usual
|
|
234
|
+
culprit) into values that end up in URLs or HTTP headers. httpx and
|
|
235
|
+
most HTTP libraries refuse to send a URL containing non-printables and
|
|
236
|
+
surface a confusing "Invalid non-printable ASCII character" error
|
|
237
|
+
instead of the real config problem.
|
|
238
|
+
|
|
239
|
+
We strip the *whole* CSI sequence (ESC + ``[`` + params + final byte)
|
|
240
|
+
before the lone-control pass — otherwise the visible tail bytes like
|
|
241
|
+
``[C`` and ``[D`` survive as ASCII garbage glued onto an otherwise
|
|
242
|
+
valid UUID, producing a clean URL that 404s instead of one the user
|
|
243
|
+
can see is wrong.
|
|
244
|
+
"""
|
|
245
|
+
if isinstance(value, str):
|
|
246
|
+
cleaned = _ANSI_CSI.sub("", value)
|
|
247
|
+
return "".join(c for c in cleaned if c >= " " and c != "\x7f")
|
|
248
|
+
return value
|
|
249
|
+
|
|
250
|
+
|
|
225
251
|
def _to_bool(value: Any) -> bool:
|
|
226
252
|
"""Convert value to boolean."""
|
|
227
253
|
if isinstance(value, bool):
|
|
@@ -488,7 +514,7 @@ def load_secrets_config(env_file: Optional[str] = None, profile: Optional[str] =
|
|
|
488
514
|
BT_SECRETS_API_URL - Gateway URL (default: https://api.beyondtrust.io)
|
|
489
515
|
BT_SECRETS_SITE_ID - Site UUID (required)
|
|
490
516
|
BT_SECRETS_PAT - Personal Access Token (required)
|
|
491
|
-
BT_SECRETS_API_VERSION - API version header (default: 2026-
|
|
517
|
+
BT_SECRETS_API_VERSION - API version header (default: 2026-04-28)
|
|
492
518
|
BT_SECRETS_VERIFY_SSL - SSL verification (default: true)
|
|
493
519
|
BT_SECRETS_TIMEOUT - Request timeout in seconds (default: 30)
|
|
494
520
|
"""
|
|
@@ -503,11 +529,15 @@ def load_secrets_config(env_file: Optional[str] = None, profile: Optional[str] =
|
|
|
503
529
|
if "pat" in layered:
|
|
504
530
|
layered["pat"] = _resolve_value(layered["pat"])
|
|
505
531
|
|
|
532
|
+
# Strip ANSI/C0 control bytes from values that flow into URLs and
|
|
533
|
+
# headers — arrow keys captured at a `bt configure` prompt have been
|
|
534
|
+
# observed to leave ESC sequences in site_id, breaking every request
|
|
535
|
+
# with a misleading "non-printable ASCII" error from httpx.
|
|
506
536
|
config = SecretsConfig(
|
|
507
|
-
api_url=layered.get("api_url") or os.getenv("BT_SECRETS_API_URL", "https://api.beyondtrust.io"),
|
|
508
|
-
site_id=layered.get("site_id") or os.getenv("BT_SECRETS_SITE_ID", ""),
|
|
509
|
-
pat=layered.get("pat") or os.getenv("BT_SECRETS_PAT", ""),
|
|
510
|
-
api_version=layered.get("api_version") or os.getenv("BT_SECRETS_API_VERSION", "2026-
|
|
537
|
+
api_url=_strip_controls(layered.get("api_url") or os.getenv("BT_SECRETS_API_URL", "https://api.beyondtrust.io")),
|
|
538
|
+
site_id=_strip_controls(layered.get("site_id") or os.getenv("BT_SECRETS_SITE_ID", "")),
|
|
539
|
+
pat=_strip_controls(layered.get("pat") or os.getenv("BT_SECRETS_PAT", "")),
|
|
540
|
+
api_version=_strip_controls(layered.get("api_version") or os.getenv("BT_SECRETS_API_VERSION", "2026-04-28")),
|
|
511
541
|
verify_ssl=_to_bool(layered.get("verify_ssl")) if "verify_ssl" in layered else _get_bool(os.getenv("BT_SECRETS_VERIFY_SSL")),
|
|
512
542
|
timeout=_to_float(layered.get("timeout")) if "timeout" in layered else _get_float(os.getenv("BT_SECRETS_TIMEOUT"), 30.0),
|
|
513
543
|
)
|
|
@@ -116,7 +116,7 @@ PRODUCTS = {
|
|
|
116
116
|
"prompt": "API version (bt-secrets-api-version header)",
|
|
117
117
|
"required": False,
|
|
118
118
|
"secret": False,
|
|
119
|
-
"default": "2026-
|
|
119
|
+
"default": "2026-04-28",
|
|
120
120
|
},
|
|
121
121
|
"verify_ssl": {"prompt": "Verify SSL", "required": False, "secret": False, "default": True},
|
|
122
122
|
"timeout": {"prompt": "Timeout (seconds)", "required": False, "secret": False, "default": 30},
|
|
@@ -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(
|
|
@@ -34,7 +34,7 @@ Set these before running commands. PAT comes from `app.beyondtrust.io`.
|
|
|
34
34
|
export BT_SECRETS_API_URL=https://api.beyondtrust.io # default
|
|
35
35
|
export BT_SECRETS_SITE_ID=<site-uuid> # required
|
|
36
36
|
export BT_SECRETS_PAT=PAT_xxx # required
|
|
37
|
-
export BT_SECRETS_API_VERSION=2026-
|
|
37
|
+
export BT_SECRETS_API_VERSION=2026-04-28 # default; doc page is stale
|
|
38
38
|
```
|
|
39
39
|
|
|
40
40
|
```bash
|
|
@@ -166,8 +166,13 @@ bt secrets integrations delete my-aws --force
|
|
|
166
166
|
- **Dynamic path is `/dynamic`, NOT `/dynamic-secrets`** — the static doc
|
|
167
167
|
page hasn't caught up to the live API. The client handles this; never
|
|
168
168
|
hand-hack URLs.
|
|
169
|
-
- **API version**: header value is `bt-secrets-api-version: 2026-
|
|
170
|
-
doc says `2026-01-02` and the server rejects it.
|
|
169
|
+
- **API version**: header value is `bt-secrets-api-version: 2026-04-28`. The
|
|
170
|
+
doc says `2026-01-02` and the server rejects it. The server also rejects
|
|
171
|
+
every earlier dated header it has retired, so an old default surfaces as
|
|
172
|
+
`API version not found: <date> is not a known API version` (helpfully the
|
|
173
|
+
error names the current version when you send one that's *newer* than
|
|
174
|
+
current — `is newer than current version <date>`). Periodic bumps are
|
|
175
|
+
mandatory; treat the default as a moving target, not a constant.
|
|
171
176
|
- **POST `/dynamic/{name}/generate` returns 201**, not 200. The client
|
|
172
177
|
accepts both via `raise_for_status` semantics.
|
|
173
178
|
- **`/leases/revoke` is scope-gated** — `bt secrets leases revoke` exits 3
|
|
@@ -220,5 +225,5 @@ directly at the requested path (this trips up newcomers — passing
|
|
|
220
225
|
|
|
221
226
|
- Base URL: `https://api.beyondtrust.io/site/<site-id>/secrets`
|
|
222
227
|
- Auth: `Authorization: Bearer <PAT>`
|
|
223
|
-
- Version header: `bt-secrets-api-version: 2026-
|
|
228
|
+
- Version header: `bt-secrets-api-version: 2026-04-28`
|
|
224
229
|
- Rate limit: 500/window — respect `x-ratelimit-reset` on 429.
|
|
@@ -14,9 +14,11 @@ from bt_cli.core.rest_debug import get_event_hooks
|
|
|
14
14
|
logger = logging.getLogger(__name__)
|
|
15
15
|
|
|
16
16
|
|
|
17
|
-
# Deltas vs the static doc page (live as of 2026-05-
|
|
18
|
-
# * Version header value is "2026-
|
|
19
|
-
# the server rejects.
|
|
17
|
+
# Deltas vs the static doc page (live as of 2026-05-14):
|
|
18
|
+
# * Version header value is "2026-04-28" — the doc claims 2026-01-02 which
|
|
19
|
+
# the server rejects. The server also rejects any earlier dated header,
|
|
20
|
+
# so a stale default surfaces as "API version not found" rather than a
|
|
21
|
+
# working-but-old response.
|
|
20
22
|
# * The dynamic-secret path is "/dynamic" not "/dynamic-secrets".
|
|
21
23
|
# * POST /dynamic/{name}/generate returns 201, not 200.
|
|
22
24
|
# * /leases/revoke is scope-gated; the lab PAT returns 403. Surface that
|
|
@@ -53,7 +53,7 @@ def status() -> None:
|
|
|
53
53
|
api_url = os.getenv("BT_SECRETS_API_URL", "https://api.beyondtrust.io")
|
|
54
54
|
site_id = os.getenv("BT_SECRETS_SITE_ID", "")
|
|
55
55
|
pat = os.getenv("BT_SECRETS_PAT", "")
|
|
56
|
-
api_version = os.getenv("BT_SECRETS_API_VERSION", "2026-
|
|
56
|
+
api_version = os.getenv("BT_SECRETS_API_VERSION", "2026-04-28")
|
|
57
57
|
|
|
58
58
|
typer.echo("Secrets API Configuration:")
|
|
59
59
|
typer.echo(f" Gateway URL: {api_url}")
|
|
@@ -18,7 +18,7 @@ def secrets_config() -> SecretsConfig:
|
|
|
18
18
|
api_url="https://mock-secrets.example.com",
|
|
19
19
|
site_id=SITE,
|
|
20
20
|
pat="PAT_test_xxxxxxxxxxxxxxxxxxxxxxxxxx",
|
|
21
|
-
api_version="2026-
|
|
21
|
+
api_version="2026-04-28",
|
|
22
22
|
verify_ssl=False,
|
|
23
23
|
timeout=5.0,
|
|
24
24
|
)
|
|
@@ -58,7 +58,7 @@ class TestHeadersAndURL:
|
|
|
58
58
|
def test_base_url_trailing_slash_normalized(self):
|
|
59
59
|
cfg = SecretsConfig(
|
|
60
60
|
api_url="https://mock-secrets.example.com/",
|
|
61
|
-
site_id=SITE, pat="PAT", api_version="2026-
|
|
61
|
+
site_id=SITE, pat="PAT", api_version="2026-04-28", verify_ssl=False,
|
|
62
62
|
)
|
|
63
63
|
c = SecretsClient(cfg)
|
|
64
64
|
assert c._url("/folders") == f"{BASE}/folders"
|
|
@@ -66,7 +66,7 @@ class TestHeadersAndURL:
|
|
|
66
66
|
def test_headers_include_bearer_and_version(self, secrets_config):
|
|
67
67
|
h = SecretsClient(secrets_config)._headers()
|
|
68
68
|
assert h["Authorization"] == "Bearer PAT_test_xxxxxxxxxxxxxxxxxxxxxxxxxx"
|
|
69
|
-
assert h["bt-secrets-api-version"] == "2026-
|
|
69
|
+
assert h["bt-secrets-api-version"] == "2026-04-28"
|
|
70
70
|
assert h["Accept"] == "application/json"
|
|
71
71
|
|
|
72
72
|
@respx.mock
|
|
@@ -77,7 +77,7 @@ class TestHeadersAndURL:
|
|
|
77
77
|
SecretsClient(secrets_config).list_folders()
|
|
78
78
|
req = route.calls[0].request
|
|
79
79
|
assert req.headers["Authorization"].startswith("Bearer PAT_test")
|
|
80
|
-
assert req.headers["bt-secrets-api-version"] == "2026-
|
|
80
|
+
assert req.headers["bt-secrets-api-version"] == "2026-04-28"
|
|
81
81
|
|
|
82
82
|
|
|
83
83
|
# ---------------------------------------------------------------------------
|
|
@@ -261,3 +261,27 @@ class TestResponseUnwrapping:
|
|
|
261
261
|
)
|
|
262
262
|
SecretsClient(secrets_config).list_integrations(type_="aws")
|
|
263
263
|
assert "type=aws" in str(route.calls[0].request.url)
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
# ---------------------------------------------------------------------------
|
|
267
|
+
# Config loader strips ANSI/C0 control bytes from URL/header-bound fields
|
|
268
|
+
# ---------------------------------------------------------------------------
|
|
269
|
+
|
|
270
|
+
class TestLoaderSanitization:
|
|
271
|
+
def test_strip_controls_removes_ansi_escapes(self, monkeypatch, tmp_path):
|
|
272
|
+
from bt_cli.core.config import load_secrets_config
|
|
273
|
+
|
|
274
|
+
# Pretend the user typed arrow keys at the `bt configure` prompt:
|
|
275
|
+
# the site_id picks up cursor-forward/back ESC sequences. Without
|
|
276
|
+
# the sanitizer these would land in the URL and httpx would reject
|
|
277
|
+
# the request with a misleading non-printable-ASCII error.
|
|
278
|
+
dirty_site = "7f735e1b-5ecb-49fa-87e8-eddf54a9c745\x1b[C\x1b[D"
|
|
279
|
+
monkeypatch.setenv("BT_SECRETS_SITE_ID", dirty_site)
|
|
280
|
+
monkeypatch.setenv("BT_SECRETS_PAT", "PAT_x")
|
|
281
|
+
monkeypatch.setenv("BT_SECRETS_API_URL", "https://api.example.com")
|
|
282
|
+
# Isolate from any real ~/.bt-cli/config.yaml on the dev box.
|
|
283
|
+
monkeypatch.setenv("HOME", str(tmp_path))
|
|
284
|
+
|
|
285
|
+
cfg = load_secrets_config()
|
|
286
|
+
assert cfg.site_id == "7f735e1b-5ecb-49fa-87e8-eddf54a9c745"
|
|
287
|
+
assert "\x1b" not in cfg.site_id
|
|
@@ -20,7 +20,7 @@ def env_overrides(monkeypatch):
|
|
|
20
20
|
monkeypatch.setenv("BT_SECRETS_API_URL", "https://mock-secrets.example.com")
|
|
21
21
|
monkeypatch.setenv("BT_SECRETS_SITE_ID", SITE)
|
|
22
22
|
monkeypatch.setenv("BT_SECRETS_PAT", "PAT_test_xxxxxxxxxxxxxxxxxxxxxxxxxx")
|
|
23
|
-
monkeypatch.setenv("BT_SECRETS_API_VERSION", "2026-
|
|
23
|
+
monkeypatch.setenv("BT_SECRETS_API_VERSION", "2026-04-28")
|
|
24
24
|
monkeypatch.setenv("BT_SECRETS_VERIFY_SSL", "false")
|
|
25
25
|
|
|
26
26
|
|
|
@@ -33,7 +33,7 @@ class TestAuth:
|
|
|
33
33
|
result = CliRunner().invoke(secrets_app, ["auth", "test"])
|
|
34
34
|
assert result.exit_code == 0
|
|
35
35
|
assert "Connected to Secrets API" in result.stdout
|
|
36
|
-
assert "2026-
|
|
36
|
+
assert "2026-04-28" in result.stdout
|
|
37
37
|
|
|
38
38
|
@respx.mock
|
|
39
39
|
def test_auth_test_401_explains_pat(self, env_overrides):
|
|
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
|