bt-cli 0.4.47__tar.gz → 0.4.49__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.47 → bt_cli-0.4.49}/CLAUDE.md +1 -1
- {bt_cli-0.4.47 → bt_cli-0.4.49}/PKG-INFO +1 -1
- {bt_cli-0.4.47 → bt_cli-0.4.49}/pyproject.toml +1 -1
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/__init__.py +1 -1
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/data/skills/secrets/SKILL.md +15 -3
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/secrets/commands/dynamic.py +69 -10
- {bt_cli-0.4.47 → bt_cli-0.4.49}/tests/secrets/test_commands.py +97 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/.claude/skills/bt/SKILL.md +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/.claude/skills/entitle/SKILL.md +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/.claude/skills/epml/SKILL.md +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/.claude/skills/epmw/SKILL.md +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/.claude/skills/pra/SKILL.md +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/.claude/skills/pws/SKILL.md +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/.env.example +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/.github/workflows/ci.yml +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/.github/workflows/release.yml +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/.gitignore +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/README.md +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/assets/cli-help.png +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/assets/cli-output.png +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/bt-cli.spec +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/bt_entry.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/epml-clients-server-side-filters-plan.md +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/epml-implementation-plan.md +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/pf-implementation-plan.md +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/scripts/bt_entry.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/scripts/pf_onboard.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/scripts/sync-package-data.sh +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/cli.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/commands/__init__.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/commands/configure.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/commands/learn.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/commands/quick.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/core/__init__.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/core/auth.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/core/client.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/core/config.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/core/config_file.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/core/csv_utils.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/core/errors.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/core/output.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/core/prompts.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/core/rest_debug.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/data/CLAUDE.md +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/data/__init__.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/data/skills/bt/SKILL.md +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/data/skills/entitle/SKILL.md +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/data/skills/epml/SKILL.md +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/data/skills/epmw/SKILL.md +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/data/skills/pra/SKILL.md +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/data/skills/pws/SKILL.md +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/entitle/__init__.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/entitle/client/__init__.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/entitle/client/base.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/entitle/commands/__init__.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/entitle/commands/accounts.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/entitle/commands/applications.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/entitle/commands/auth.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/entitle/commands/bundles.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/entitle/commands/integrations.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/entitle/commands/permissions.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/entitle/commands/policies.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/entitle/commands/requests.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/entitle/commands/resources.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/entitle/commands/roles.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/entitle/commands/users.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/entitle/commands/workflows.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/entitle/models/__init__.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/entitle/models/bundle.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/entitle/models/common.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/entitle/models/integration.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/entitle/models/permission.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/entitle/models/policy.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/entitle/models/resource.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/entitle/models/role.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/entitle/models/user.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/entitle/models/workflow.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/epml/__init__.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/epml/client/__init__.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/epml/client/base.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/epml/commands/__init__.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/epml/commands/audit.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/epml/commands/auth.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/epml/commands/client_pkg.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/epml/commands/clients.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/epml/commands/external_apis.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/epml/commands/hosts.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/epml/commands/iolog.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/epml/commands/license.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/epml/commands/quick.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/epml/commands/rbp_cmdgrps.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/epml/commands/rbp_entitlement.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/epml/commands/rbp_hostgrps.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/epml/commands/rbp_policy.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/epml/commands/rbp_roles.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/epml/commands/rbp_tests.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/epml/commands/rbp_tmdategrps.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/epml/commands/rbp_tx.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/epml/commands/rbp_usergrps.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/epml/commands/settings.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/epml/commands/siems.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/epml/commands/users.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/epml/models/__init__.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/epmw/__init__.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/epmw/client/__init__.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/epmw/client/base.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/epmw/commands/__init__.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/epmw/commands/audits.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/epmw/commands/auth.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/epmw/commands/computers.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/epmw/commands/events.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/epmw/commands/groups.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/epmw/commands/policies.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/epmw/commands/quick.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/epmw/commands/requests.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/epmw/commands/roles.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/epmw/commands/tasks.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/epmw/commands/users.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/epmw/models/__init__.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/pra/__init__.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/pra/client/__init__.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/pra/client/base.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/pra/commands/__init__.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/pra/commands/auth.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/pra/commands/group_policies.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/pra/commands/import_export.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/pra/commands/jump_clients.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/pra/commands/jump_groups.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/pra/commands/jump_items.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/pra/commands/jumpoints.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/pra/commands/policies.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/pra/commands/quick.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/pra/commands/teams.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/pra/commands/users.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/pra/commands/vault.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/pra/models/__init__.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/pra/models/common.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/pra/models/group_policy.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/pra/models/jump_client.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/pra/models/jump_group.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/pra/models/jump_item.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/pra/models/jumpoint.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/pra/models/team.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/pra/models/user.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/pra/models/vault.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/pws/__init__.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/pws/client/__init__.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/pws/client/base.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/pws/client/beyondinsight.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/pws/client/passwordsafe.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/pws/commands/__init__.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/pws/commands/accounts.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/pws/commands/assets.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/pws/commands/attributes.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/pws/commands/auth.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/pws/commands/clouds.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/pws/commands/config.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/pws/commands/credentials.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/pws/commands/databases.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/pws/commands/directories.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/pws/commands/functional.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/pws/commands/import_export.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/pws/commands/platforms.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/pws/commands/quick.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/pws/commands/search.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/pws/commands/secrets.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/pws/commands/systems.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/pws/commands/users.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/pws/commands/workgroups.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/pws/config.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/pws/models/__init__.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/pws/models/account.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/pws/models/asset.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/pws/models/common.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/pws/models/system.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/secrets/__init__.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/secrets/client/__init__.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/secrets/client/base.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/secrets/commands/__init__.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/secrets/commands/_hints.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/secrets/commands/auth.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/secrets/commands/folders.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/secrets/commands/integrations.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/secrets/commands/leases.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/secrets/commands/static.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/src/bt_cli/secrets/models/__init__.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/tests/__init__.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/tests/conftest.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/tests/core/__init__.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/tests/core/test_auth.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/tests/core/test_config.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/tests/core/test_errors.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/tests/core/test_rest_debug.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/tests/entitle/__init__.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/tests/entitle/test_client.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/tests/entitle/test_commands.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/tests/entitle-smoke-test.sh +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/tests/epml/__init__.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/tests/epml/test_client.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/tests/epml/test_commands.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/tests/epmw/__init__.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/tests/epmw/test_client.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/tests/epmw/test_commands.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/tests/epmw-quick-test-plan.md +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/tests/fixtures/__init__.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/tests/fixtures/responses.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/tests/integration/__init__.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/tests/integration/conftest.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/tests/integration/helpers.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/tests/integration/test_entitle_integration.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/tests/integration/test_epmw_integration.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/tests/integration/test_epmw_lifecycle.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/tests/integration/test_pra_integration.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/tests/integration/test_pra_lifecycle.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/tests/integration/test_pws_integration.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/tests/integration/test_pws_lifecycle.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/tests/pra/__init__.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/tests/pra/test_client.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/tests/pra/test_commands.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/tests/pra-smoke-test.sh +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/tests/pra-test-plan.md +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/tests/pws/__init__.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/tests/pws/test_client.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/tests/pws/test_commands.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/tests/pws-quick-test-plan.md +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/tests/pws-smoke-test.sh +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/tests/secrets/__init__.py +0 -0
- {bt_cli-0.4.47 → bt_cli-0.4.49}/tests/secrets/test_client.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.49**
|
|
4
4
|
|
|
5
5
|
## Setup
|
|
6
6
|
|
|
@@ -97,12 +97,24 @@ bt secrets dynamic get lab/aws/ec2-readonly
|
|
|
97
97
|
|
|
98
98
|
# Mint fresh credentials — default prints shell `export AWS_*` lines.
|
|
99
99
|
bt secrets dynamic generate lab/aws/ec2-readonly
|
|
100
|
-
|
|
101
|
-
|
|
100
|
+
|
|
101
|
+
# Apply to the current session (a child process can't mutate its parent's env,
|
|
102
|
+
# so the CLI emits shell-native syntax that you eval into your shell):
|
|
103
|
+
|
|
104
|
+
# bash / zsh:
|
|
105
|
+
eval $(bt secrets dynamic generate lab/aws/ec2-readonly)
|
|
106
|
+
|
|
107
|
+
# PowerShell:
|
|
108
|
+
bt secrets dynamic generate lab/aws/ec2-readonly --format powershell | iex
|
|
109
|
+
|
|
110
|
+
# cmd.exe:
|
|
111
|
+
for /f "delims=" %i in ('bt secrets dynamic generate lab/aws/ec2-readonly --format cmd') do %i
|
|
112
|
+
|
|
113
|
+
aws sts get-caller-identity # verify the creds are active
|
|
102
114
|
|
|
103
115
|
# Other formats:
|
|
104
116
|
bt secrets dynamic generate lab/aws/ec2-readonly --format json # full payload w/ leaseId
|
|
105
|
-
bt secrets dynamic generate lab/aws/ec2-readonly --format env # KEY=value
|
|
117
|
+
bt secrets dynamic generate lab/aws/ec2-readonly --format env # bare KEY=value
|
|
106
118
|
bt secrets dynamic generate lab/aws/ec2-readonly --format dotenv # KEY="value" for .env files
|
|
107
119
|
```
|
|
108
120
|
|
|
@@ -28,10 +28,12 @@ app = typer.Typer(no_args_is_help=True, help="Dynamic secrets (e.g. AWS STS, min
|
|
|
28
28
|
class GenerateFormat(str, Enum):
|
|
29
29
|
"""Output formats for `dynamic generate`."""
|
|
30
30
|
|
|
31
|
-
EXPORT = "export"
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
31
|
+
EXPORT = "export" # default — bash/zsh `export AWS_*` lines
|
|
32
|
+
POWERSHELL = "powershell" # PowerShell `$env:AWS_* = "..."` lines
|
|
33
|
+
CMD = "cmd" # Windows cmd.exe `set AWS_*=...` lines
|
|
34
|
+
ENV = "env" # bare KEY=value lines (no `export`)
|
|
35
|
+
JSON = "json" # raw API payload
|
|
36
|
+
DOTENV = "dotenv" # KEY="value" lines suitable for .env files
|
|
35
37
|
|
|
36
38
|
|
|
37
39
|
def _dynamic_row(item: dict) -> dict:
|
|
@@ -44,6 +46,32 @@ def _dynamic_row(item: dict) -> dict:
|
|
|
44
46
|
}
|
|
45
47
|
|
|
46
48
|
|
|
49
|
+
def _apply_hint(full_path: str, fmt: GenerateFormat) -> str:
|
|
50
|
+
"""Build a one-line apply-recipe in syntax valid for the chosen format.
|
|
51
|
+
|
|
52
|
+
`eval $(...)`, `| iex`, and `for /f` all swallow comment lines harmlessly,
|
|
53
|
+
so embedding the hint costs nothing when the output is piped through —
|
|
54
|
+
it's just discoverable for users who run the command on its own first.
|
|
55
|
+
|
|
56
|
+
Non-shell formats (env / dotenv / json) get no hint: env/dotenv are for
|
|
57
|
+
writing to files, json is for scripts; neither has a single canonical
|
|
58
|
+
apply command.
|
|
59
|
+
"""
|
|
60
|
+
if fmt is GenerateFormat.EXPORT:
|
|
61
|
+
return f"# Tip: apply with -> eval $(bt secrets dynamic generate {full_path})"
|
|
62
|
+
if fmt is GenerateFormat.POWERSHELL:
|
|
63
|
+
return (
|
|
64
|
+
f"# Tip: apply with -> "
|
|
65
|
+
f"bt secrets dynamic generate {full_path} --format powershell | iex"
|
|
66
|
+
)
|
|
67
|
+
if fmt is GenerateFormat.CMD:
|
|
68
|
+
return (
|
|
69
|
+
f'REM Tip: apply with -> for /f "delims=" %i in '
|
|
70
|
+
f"('bt secrets dynamic generate {full_path} --format cmd') do %i"
|
|
71
|
+
)
|
|
72
|
+
return ""
|
|
73
|
+
|
|
74
|
+
|
|
47
75
|
def _format_credentials(secret: dict, fmt: GenerateFormat) -> str:
|
|
48
76
|
"""Format an AWS STS credential triplet in the requested shape.
|
|
49
77
|
|
|
@@ -51,6 +79,13 @@ def _format_credentials(secret: dict, fmt: GenerateFormat) -> str:
|
|
|
51
79
|
(``accessKeyId``, ``secretAccessKey``, ``sessionToken``). For shell
|
|
52
80
|
consumption we map those to the standard ``AWS_*`` env-var names that
|
|
53
81
|
boto3, aws-cli, and the EC2 SDK all read.
|
|
82
|
+
|
|
83
|
+
A subprocess can't mutate its parent shell's environment, so each
|
|
84
|
+
format is designed to be piped into the shell's eval mechanism:
|
|
85
|
+
|
|
86
|
+
* EXPORT → `eval $(bt secrets dynamic generate <path>)`
|
|
87
|
+
* POWERSHELL → `bt secrets dynamic generate <path> --format powershell | iex`
|
|
88
|
+
* CMD → cmd.exe needs a temp .bat or a `for /f` loop — see SKILL.md
|
|
54
89
|
"""
|
|
55
90
|
aws_map = [
|
|
56
91
|
("AWS_ACCESS_KEY_ID", "accessKeyId"),
|
|
@@ -63,6 +98,16 @@ def _format_credentials(secret: dict, fmt: GenerateFormat) -> str:
|
|
|
63
98
|
value = secret[payload_key]
|
|
64
99
|
if fmt is GenerateFormat.EXPORT:
|
|
65
100
|
lines.append(f"export {env_name}={value}")
|
|
101
|
+
elif fmt is GenerateFormat.POWERSHELL:
|
|
102
|
+
# `Invoke-Expression` parses these as statements that set the
|
|
103
|
+
# process env. Quote the value so PowerShell doesn't try to
|
|
104
|
+
# interpret embedded characters.
|
|
105
|
+
lines.append(f'$env:{env_name} = "{value}"')
|
|
106
|
+
elif fmt is GenerateFormat.CMD:
|
|
107
|
+
# `set` in cmd.exe doesn't quote and doesn't tolerate spaces
|
|
108
|
+
# around `=`. Values from STS are alphanumeric-plus-/+=_-, so
|
|
109
|
+
# bare assignment is safe.
|
|
110
|
+
lines.append(f"set {env_name}={value}")
|
|
66
111
|
elif fmt is GenerateFormat.ENV:
|
|
67
112
|
lines.append(f"{env_name}={value}")
|
|
68
113
|
elif fmt is GenerateFormat.DOTENV:
|
|
@@ -70,8 +115,13 @@ def _format_credentials(secret: dict, fmt: GenerateFormat) -> str:
|
|
|
70
115
|
if not lines:
|
|
71
116
|
return ""
|
|
72
117
|
if "expiration" in secret:
|
|
73
|
-
# Trailing comment is
|
|
74
|
-
|
|
118
|
+
# Trailing comment is informational — all four script-y formats
|
|
119
|
+
# tolerate `#` (bash/PowerShell) or treat the line as a label-style
|
|
120
|
+
# noop for cmd's set, which we replace with REM below.
|
|
121
|
+
if fmt is GenerateFormat.CMD:
|
|
122
|
+
lines.append(f"REM expires: {secret['expiration']}")
|
|
123
|
+
else:
|
|
124
|
+
lines.append(f"# expires: {secret['expiration']}")
|
|
75
125
|
return "\n".join(lines)
|
|
76
126
|
|
|
77
127
|
|
|
@@ -150,15 +200,21 @@ def generate(
|
|
|
150
200
|
full_path: str = typer.Argument(..., help="Full dynamic-secret path"),
|
|
151
201
|
format_: GenerateFormat = typer.Option(
|
|
152
202
|
GenerateFormat.EXPORT, "--format", "-F",
|
|
153
|
-
help="Output format: export | env | json | dotenv",
|
|
203
|
+
help="Output format: export | powershell | cmd | env | json | dotenv",
|
|
154
204
|
case_sensitive=False,
|
|
155
205
|
),
|
|
156
206
|
) -> None:
|
|
157
207
|
"""Mint fresh credentials from a dynamic-secret config.
|
|
158
208
|
|
|
159
|
-
Default ``--format export`` prints shell ``export AWS_*`` lines
|
|
160
|
-
|
|
161
|
-
|
|
209
|
+
Default ``--format export`` prints shell ``export AWS_*`` lines.
|
|
210
|
+
|
|
211
|
+
\b
|
|
212
|
+
Apply in your current session:
|
|
213
|
+
bash / zsh: eval $(bt secrets dynamic generate <path>)
|
|
214
|
+
PowerShell: bt secrets dynamic generate <path> --format powershell | iex
|
|
215
|
+
cmd.exe: for /f "delims=" %i in ('bt secrets dynamic generate <path> --format cmd') do %i
|
|
216
|
+
|
|
217
|
+
Use ``--format json`` to see the full payload (lease id, expiration).
|
|
162
218
|
|
|
163
219
|
Heads up: every call mints real STS credentials and shows up in the
|
|
164
220
|
audit trail. Don't loop this in development.
|
|
@@ -197,6 +253,9 @@ def generate(
|
|
|
197
253
|
print_error("Unrecognized credential shape; printing raw payload")
|
|
198
254
|
print_json(secret)
|
|
199
255
|
raise typer.Exit(1)
|
|
256
|
+
hint = _apply_hint(full_path, format_)
|
|
257
|
+
if hint:
|
|
258
|
+
text = f"{text}\n{hint}"
|
|
200
259
|
typer.echo(text)
|
|
201
260
|
|
|
202
261
|
|
|
@@ -194,6 +194,57 @@ class TestDynamic:
|
|
|
194
194
|
assert "export AWS_SESSION_TOKEN=TOK-FAKE" in result.stdout
|
|
195
195
|
# PAT must not leak in any form
|
|
196
196
|
assert "PAT_test" not in result.stdout
|
|
197
|
+
# Inline apply-hint includes the actual path the user passed
|
|
198
|
+
assert "eval $(bt secrets dynamic generate lab/aws/ec2-readonly)" in result.stdout
|
|
199
|
+
|
|
200
|
+
@respx.mock
|
|
201
|
+
def test_dynamic_generate_powershell_hint(self, env_overrides):
|
|
202
|
+
respx.post(_url("/dynamic/ec2-readonly/generate")).mock(
|
|
203
|
+
return_value=httpx.Response(201, json={"secret": {
|
|
204
|
+
"accessKeyId": "ASIA-FAKE", "secretAccessKey": "SAK-FAKE",
|
|
205
|
+
"sessionToken": "TOK-FAKE", "expiration": "2026-01-01T00:00:00Z",
|
|
206
|
+
}})
|
|
207
|
+
)
|
|
208
|
+
result = CliRunner().invoke(
|
|
209
|
+
secrets_app, ["dynamic", "generate", "lab/aws/ec2-readonly", "--format", "powershell"],
|
|
210
|
+
)
|
|
211
|
+
assert result.exit_code == 0
|
|
212
|
+
assert "--format powershell | iex" in result.stdout
|
|
213
|
+
# Hint must use # (valid PowerShell comment); not REM
|
|
214
|
+
assert "REM Tip" not in result.stdout
|
|
215
|
+
|
|
216
|
+
@respx.mock
|
|
217
|
+
def test_dynamic_generate_cmd_hint(self, env_overrides):
|
|
218
|
+
respx.post(_url("/dynamic/ec2-readonly/generate")).mock(
|
|
219
|
+
return_value=httpx.Response(201, json={"secret": {
|
|
220
|
+
"accessKeyId": "ASIA-FAKE", "secretAccessKey": "SAK-FAKE",
|
|
221
|
+
"sessionToken": "TOK-FAKE", "expiration": "2026-01-01T00:00:00Z",
|
|
222
|
+
}})
|
|
223
|
+
)
|
|
224
|
+
result = CliRunner().invoke(
|
|
225
|
+
secrets_app, ["dynamic", "generate", "lab/aws/ec2-readonly", "--format", "cmd"],
|
|
226
|
+
)
|
|
227
|
+
assert result.exit_code == 0
|
|
228
|
+
assert "for /f" in result.stdout
|
|
229
|
+
assert "--format cmd" in result.stdout
|
|
230
|
+
# cmd format must use REM (not #) for the apply hint
|
|
231
|
+
assert "REM Tip" in result.stdout
|
|
232
|
+
|
|
233
|
+
@respx.mock
|
|
234
|
+
def test_dynamic_generate_no_hint_for_non_shell_formats(self, env_overrides):
|
|
235
|
+
"""env / dotenv / json shouldn't carry an apply hint — no canonical apply mode."""
|
|
236
|
+
respx.post(_url("/dynamic/ec2-readonly/generate")).mock(
|
|
237
|
+
return_value=httpx.Response(201, json={"secret": {
|
|
238
|
+
"accessKeyId": "ASIA-FAKE", "secretAccessKey": "SAK-FAKE",
|
|
239
|
+
"sessionToken": "TOK-FAKE", "expiration": "2026-01-01T00:00:00Z",
|
|
240
|
+
}})
|
|
241
|
+
)
|
|
242
|
+
for fmt in ("env", "dotenv"):
|
|
243
|
+
result = CliRunner().invoke(
|
|
244
|
+
secrets_app, ["dynamic", "generate", "lab/aws/ec2-readonly", "--format", fmt],
|
|
245
|
+
)
|
|
246
|
+
assert result.exit_code == 0, fmt
|
|
247
|
+
assert "Tip: apply with" not in result.stdout, fmt
|
|
197
248
|
|
|
198
249
|
@respx.mock
|
|
199
250
|
def test_dynamic_generate_json(self, env_overrides):
|
|
@@ -226,6 +277,52 @@ class TestDynamic:
|
|
|
226
277
|
assert "AWS_ACCESS_KEY_ID=AKIA-FAKE" in result.stdout
|
|
227
278
|
assert "export" not in result.stdout.split("\n")[0]
|
|
228
279
|
|
|
280
|
+
@respx.mock
|
|
281
|
+
def test_dynamic_generate_powershell_shape(self, env_overrides):
|
|
282
|
+
"""PowerShell format: $env:VAR = "val" — pipe to Invoke-Expression."""
|
|
283
|
+
respx.post(_url("/dynamic/ec2-readonly/generate")).mock(
|
|
284
|
+
return_value=httpx.Response(201, json={"secret": {
|
|
285
|
+
"accessKeyId": "ASIA-FAKE",
|
|
286
|
+
"secretAccessKey": "SAK-FAKE",
|
|
287
|
+
"sessionToken": "TOK-FAKE",
|
|
288
|
+
"expiration": "2026-01-01T00:00:00Z",
|
|
289
|
+
}})
|
|
290
|
+
)
|
|
291
|
+
result = CliRunner().invoke(
|
|
292
|
+
secrets_app,
|
|
293
|
+
["dynamic", "generate", "lab/aws/ec2-readonly", "--format", "powershell"],
|
|
294
|
+
)
|
|
295
|
+
assert result.exit_code == 0
|
|
296
|
+
assert '$env:AWS_ACCESS_KEY_ID = "ASIA-FAKE"' in result.stdout
|
|
297
|
+
assert '$env:AWS_SECRET_ACCESS_KEY = "SAK-FAKE"' in result.stdout
|
|
298
|
+
assert '$env:AWS_SESSION_TOKEN = "TOK-FAKE"' in result.stdout
|
|
299
|
+
# Comment must use `#` (valid PowerShell line comment)
|
|
300
|
+
assert "# expires:" in result.stdout
|
|
301
|
+
# Must NOT contain bash `export` syntax
|
|
302
|
+
assert "export AWS_" not in result.stdout
|
|
303
|
+
|
|
304
|
+
@respx.mock
|
|
305
|
+
def test_dynamic_generate_cmd_shape(self, env_overrides):
|
|
306
|
+
"""cmd.exe format: set VAR=val — for `for /f` extraction."""
|
|
307
|
+
respx.post(_url("/dynamic/ec2-readonly/generate")).mock(
|
|
308
|
+
return_value=httpx.Response(201, json={"secret": {
|
|
309
|
+
"accessKeyId": "ASIA-FAKE",
|
|
310
|
+
"secretAccessKey": "SAK-FAKE",
|
|
311
|
+
"sessionToken": "TOK-FAKE",
|
|
312
|
+
"expiration": "2026-01-01T00:00:00Z",
|
|
313
|
+
}})
|
|
314
|
+
)
|
|
315
|
+
result = CliRunner().invoke(
|
|
316
|
+
secrets_app,
|
|
317
|
+
["dynamic", "generate", "lab/aws/ec2-readonly", "--format", "cmd"],
|
|
318
|
+
)
|
|
319
|
+
assert result.exit_code == 0
|
|
320
|
+
assert "set AWS_ACCESS_KEY_ID=ASIA-FAKE" in result.stdout
|
|
321
|
+
assert "set AWS_SECRET_ACCESS_KEY=SAK-FAKE" in result.stdout
|
|
322
|
+
# cmd.exe uses REM, not #, for comments
|
|
323
|
+
assert "REM expires:" in result.stdout
|
|
324
|
+
assert "# expires" not in result.stdout
|
|
325
|
+
|
|
229
326
|
|
|
230
327
|
class TestLeases:
|
|
231
328
|
@respx.mock
|
|
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
|