bt-cli 0.4.54__tar.gz → 0.4.55__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.54 → bt_cli-0.4.55}/.github/workflows/ci.yml +7 -7
- {bt_cli-0.4.54 → bt_cli-0.4.55}/.github/workflows/release.yml +12 -12
- {bt_cli-0.4.54 → bt_cli-0.4.55}/CLAUDE.md +6 -2
- {bt_cli-0.4.54 → bt_cli-0.4.55}/PKG-INFO +1 -1
- {bt_cli-0.4.54 → bt_cli-0.4.55}/pyproject.toml +1 -1
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/__init__.py +1 -1
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/cli.py +51 -2
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/commands/configure.py +4 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/core/config.py +64 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/core/config_file.py +31 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/data/skills/epml/SKILL.md +12 -0
- bt_cli-0.4.55/src/bt_cli/data/skills/pf/SKILL.md +76 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/epml/client/base.py +43 -0
- bt_cli-0.4.55/src/bt_cli/epml/commands/client_pkg.py +278 -0
- bt_cli-0.4.55/src/bt_cli/epml/commands/quick.py +139 -0
- bt_cli-0.4.55/src/bt_cli/pf/__init__.py +1 -0
- bt_cli-0.4.55/src/bt_cli/pf/client/__init__.py +5 -0
- bt_cli-0.4.55/src/bt_cli/pf/client/base.py +133 -0
- bt_cli-0.4.55/src/bt_cli/pf/commands/__init__.py +23 -0
- bt_cli-0.4.55/src/bt_cli/pf/commands/auth.py +35 -0
- bt_cli-0.4.55/src/bt_cli/pf/commands/machines.py +83 -0
- bt_cli-0.4.55/src/bt_cli/pf/commands/tokens.py +99 -0
- bt_cli-0.4.55/src/bt_cli/pf/commands/user.py +70 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/tests/epml/test_client.py +44 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/tests/epml/test_commands.py +87 -0
- bt_cli-0.4.55/tests/pf/test_client.py +79 -0
- bt_cli-0.4.55/tests/pf/test_commands.py +121 -0
- bt_cli-0.4.55/tests/pws/__init__.py +0 -0
- bt_cli-0.4.54/src/bt_cli/epml/commands/client_pkg.py +0 -99
- bt_cli-0.4.54/src/bt_cli/epml/commands/quick.py +0 -67
- {bt_cli-0.4.54 → bt_cli-0.4.55}/.claude/skills/bt/SKILL.md +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/.claude/skills/entitle/SKILL.md +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/.claude/skills/epml/SKILL.md +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/.claude/skills/epmw/SKILL.md +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/.claude/skills/pra/SKILL.md +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/.claude/skills/pws/SKILL.md +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/.env.example +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/.gitignore +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/README.md +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/assets/cli-help.png +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/assets/cli-output.png +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/bt-cli.spec +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/bt_entry.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/epml-clients-server-side-filters-plan.md +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/epml-implementation-plan.md +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/pf-implementation-plan.md +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/scripts/bt_entry.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/scripts/pf_onboard.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/scripts/sync-package-data.sh +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/commands/__init__.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/commands/learn.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/commands/quick.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/core/__init__.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/core/auth.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/core/client.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/core/csv_utils.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/core/errors.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/core/output.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/core/prompts.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/core/rest_debug.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/data/CLAUDE.md +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/data/__init__.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/data/skills/bt/SKILL.md +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/data/skills/entitle/SKILL.md +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/data/skills/epmw/SKILL.md +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/data/skills/pra/SKILL.md +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/data/skills/pws/SKILL.md +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/data/skills/secrets/SKILL.md +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/entitle/__init__.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/entitle/client/__init__.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/entitle/client/base.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/entitle/commands/__init__.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/entitle/commands/accounts.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/entitle/commands/applications.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/entitle/commands/auth.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/entitle/commands/bundles.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/entitle/commands/integrations.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/entitle/commands/permissions.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/entitle/commands/policies.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/entitle/commands/requests.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/entitle/commands/resources.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/entitle/commands/roles.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/entitle/commands/users.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/entitle/commands/workflows.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/entitle/models/__init__.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/entitle/models/bundle.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/entitle/models/common.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/entitle/models/integration.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/entitle/models/permission.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/entitle/models/policy.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/entitle/models/resource.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/entitle/models/role.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/entitle/models/user.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/entitle/models/workflow.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/epml/__init__.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/epml/client/__init__.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/epml/commands/__init__.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/epml/commands/audit.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/epml/commands/auth.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/epml/commands/clients.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/epml/commands/external_apis.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/epml/commands/hosts.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/epml/commands/iolog.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/epml/commands/license.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/epml/commands/rbp_cmdgrps.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/epml/commands/rbp_entitlement.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/epml/commands/rbp_hostgrps.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/epml/commands/rbp_policy.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/epml/commands/rbp_roles.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/epml/commands/rbp_tests.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/epml/commands/rbp_tmdategrps.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/epml/commands/rbp_tx.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/epml/commands/rbp_usergrps.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/epml/commands/settings.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/epml/commands/siems.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/epml/commands/users.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/epml/models/__init__.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/epmw/__init__.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/epmw/client/__init__.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/epmw/client/base.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/epmw/commands/__init__.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/epmw/commands/audits.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/epmw/commands/auth.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/epmw/commands/computers.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/epmw/commands/events.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/epmw/commands/groups.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/epmw/commands/policies.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/epmw/commands/quick.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/epmw/commands/requests.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/epmw/commands/roles.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/epmw/commands/tasks.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/epmw/commands/users.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/epmw/models/__init__.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/pra/__init__.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/pra/client/__init__.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/pra/client/base.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/pra/commands/__init__.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/pra/commands/auth.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/pra/commands/group_policies.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/pra/commands/import_export.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/pra/commands/jump_clients.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/pra/commands/jump_groups.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/pra/commands/jump_items.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/pra/commands/jumpoints.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/pra/commands/policies.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/pra/commands/quick.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/pra/commands/teams.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/pra/commands/users.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/pra/commands/vault.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/pra/models/__init__.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/pra/models/common.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/pra/models/group_policy.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/pra/models/jump_client.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/pra/models/jump_group.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/pra/models/jump_item.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/pra/models/jumpoint.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/pra/models/team.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/pra/models/user.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/pra/models/vault.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/pws/__init__.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/pws/client/__init__.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/pws/client/base.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/pws/client/beyondinsight.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/pws/client/passwordsafe.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/pws/commands/__init__.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/pws/commands/accounts.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/pws/commands/assets.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/pws/commands/attributes.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/pws/commands/auth.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/pws/commands/clouds.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/pws/commands/config.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/pws/commands/credentials.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/pws/commands/databases.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/pws/commands/directories.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/pws/commands/functional.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/pws/commands/import_export.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/pws/commands/platforms.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/pws/commands/quick.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/pws/commands/search.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/pws/commands/secrets.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/pws/commands/systems.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/pws/commands/users.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/pws/commands/workgroups.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/pws/config.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/pws/models/__init__.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/pws/models/account.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/pws/models/asset.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/pws/models/common.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/pws/models/system.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/secrets/__init__.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/secrets/client/__init__.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/secrets/client/base.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/secrets/commands/__init__.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/secrets/commands/_hints.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/secrets/commands/auth.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/secrets/commands/dynamic.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/secrets/commands/folders.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/secrets/commands/integrations.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/secrets/commands/leases.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/secrets/commands/static.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/src/bt_cli/secrets/models/__init__.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/tests/__init__.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/tests/conftest.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/tests/core/__init__.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/tests/core/test_auth.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/tests/core/test_config.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/tests/core/test_config_file.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/tests/core/test_errors.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/tests/core/test_output.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/tests/core/test_rest_debug.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/tests/entitle/__init__.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/tests/entitle/test_client.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/tests/entitle/test_commands.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/tests/entitle-smoke-test.sh +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/tests/epml/__init__.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/tests/epmw/__init__.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/tests/epmw/test_client.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/tests/epmw/test_commands.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/tests/epmw-quick-test-plan.md +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/tests/fixtures/__init__.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/tests/fixtures/responses.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/tests/integration/__init__.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/tests/integration/conftest.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/tests/integration/helpers.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/tests/integration/test_entitle_integration.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/tests/integration/test_epmw_integration.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/tests/integration/test_epmw_lifecycle.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/tests/integration/test_pra_integration.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/tests/integration/test_pra_lifecycle.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/tests/integration/test_pws_integration.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/tests/integration/test_pws_lifecycle.py +0 -0
- {bt_cli-0.4.54/tests/pra → bt_cli-0.4.55/tests/pf}/__init__.py +0 -0
- {bt_cli-0.4.54/tests/pws → bt_cli-0.4.55/tests/pra}/__init__.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/tests/pra/test_client.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/tests/pra/test_commands.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/tests/pra-smoke-test.sh +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/tests/pra-test-plan.md +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/tests/pws/test_client.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/tests/pws/test_commands.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/tests/pws-quick-test-plan.md +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/tests/pws-smoke-test.sh +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/tests/secrets/__init__.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/tests/secrets/test_client.py +0 -0
- {bt_cli-0.4.54 → bt_cli-0.4.55}/tests/secrets/test_commands.py +0 -0
|
@@ -16,10 +16,10 @@ jobs:
|
|
|
16
16
|
python-version: ['3.10', '3.11', '3.12']
|
|
17
17
|
|
|
18
18
|
steps:
|
|
19
|
-
- uses: actions/checkout@
|
|
19
|
+
- uses: actions/checkout@v5
|
|
20
20
|
|
|
21
21
|
- name: Set up Python ${{ matrix.python-version }}
|
|
22
|
-
uses: actions/setup-python@
|
|
22
|
+
uses: actions/setup-python@v6
|
|
23
23
|
with:
|
|
24
24
|
python-version: ${{ matrix.python-version }}
|
|
25
25
|
|
|
@@ -48,7 +48,7 @@ jobs:
|
|
|
48
48
|
pytest tests/ --ignore=tests/integration -v --cov=bt_cli --cov-report=term-missing --cov-report=xml
|
|
49
49
|
|
|
50
50
|
- name: Upload coverage to Codecov
|
|
51
|
-
uses: codecov/codecov-action@
|
|
51
|
+
uses: codecov/codecov-action@v5
|
|
52
52
|
if: matrix.python-version == '3.12'
|
|
53
53
|
with:
|
|
54
54
|
files: ./coverage.xml
|
|
@@ -58,10 +58,10 @@ jobs:
|
|
|
58
58
|
runs-on: ubuntu-latest
|
|
59
59
|
|
|
60
60
|
steps:
|
|
61
|
-
- uses: actions/checkout@
|
|
61
|
+
- uses: actions/checkout@v5
|
|
62
62
|
|
|
63
63
|
- name: Set up Python
|
|
64
|
-
uses: actions/setup-python@
|
|
64
|
+
uses: actions/setup-python@v6
|
|
65
65
|
with:
|
|
66
66
|
python-version: '3.11'
|
|
67
67
|
|
|
@@ -87,10 +87,10 @@ jobs:
|
|
|
87
87
|
if: github.event_name == 'workflow_dispatch' || github.event_name == 'schedule'
|
|
88
88
|
|
|
89
89
|
steps:
|
|
90
|
-
- uses: actions/checkout@
|
|
90
|
+
- uses: actions/checkout@v5
|
|
91
91
|
|
|
92
92
|
- name: Set up Python
|
|
93
|
-
uses: actions/setup-python@
|
|
93
|
+
uses: actions/setup-python@v6
|
|
94
94
|
with:
|
|
95
95
|
python-version: '3.12'
|
|
96
96
|
|
|
@@ -28,7 +28,7 @@ jobs:
|
|
|
28
28
|
image: python:3.11-slim-bullseye
|
|
29
29
|
|
|
30
30
|
steps:
|
|
31
|
-
- uses: actions/checkout@
|
|
31
|
+
- uses: actions/checkout@v5
|
|
32
32
|
|
|
33
33
|
- name: Install build dependencies
|
|
34
34
|
run: |
|
|
@@ -44,7 +44,7 @@ jobs:
|
|
|
44
44
|
run: mv dist/bt dist/bt-linux-amd64
|
|
45
45
|
|
|
46
46
|
- name: Upload artifact
|
|
47
|
-
uses: actions/upload-artifact@
|
|
47
|
+
uses: actions/upload-artifact@v7
|
|
48
48
|
with:
|
|
49
49
|
name: bt-linux-amd64
|
|
50
50
|
path: dist/bt-linux-amd64
|
|
@@ -67,10 +67,10 @@ jobs:
|
|
|
67
67
|
runs-on: ${{ matrix.os }}
|
|
68
68
|
|
|
69
69
|
steps:
|
|
70
|
-
- uses: actions/checkout@
|
|
70
|
+
- uses: actions/checkout@v5
|
|
71
71
|
|
|
72
72
|
- name: Set up Python
|
|
73
|
-
uses: actions/setup-python@
|
|
73
|
+
uses: actions/setup-python@v6
|
|
74
74
|
with:
|
|
75
75
|
python-version: '3.11'
|
|
76
76
|
|
|
@@ -92,7 +92,7 @@ jobs:
|
|
|
92
92
|
run: move dist\bt.exe dist\${{ matrix.asset_name }}
|
|
93
93
|
|
|
94
94
|
- name: Upload artifact
|
|
95
|
-
uses: actions/upload-artifact@
|
|
95
|
+
uses: actions/upload-artifact@v7
|
|
96
96
|
with:
|
|
97
97
|
name: ${{ matrix.asset_name }}
|
|
98
98
|
path: dist/${{ matrix.asset_name }}
|
|
@@ -102,10 +102,10 @@ jobs:
|
|
|
102
102
|
runs-on: ubuntu-latest
|
|
103
103
|
|
|
104
104
|
steps:
|
|
105
|
-
- uses: actions/checkout@
|
|
105
|
+
- uses: actions/checkout@v5
|
|
106
106
|
|
|
107
107
|
- name: Set up Python
|
|
108
|
-
uses: actions/setup-python@
|
|
108
|
+
uses: actions/setup-python@v6
|
|
109
109
|
with:
|
|
110
110
|
python-version: '3.11'
|
|
111
111
|
|
|
@@ -121,7 +121,7 @@ jobs:
|
|
|
121
121
|
run: twine check dist/*
|
|
122
122
|
|
|
123
123
|
- name: Upload package artifact
|
|
124
|
-
uses: actions/upload-artifact@
|
|
124
|
+
uses: actions/upload-artifact@v7
|
|
125
125
|
with:
|
|
126
126
|
name: python-package
|
|
127
127
|
path: dist/*
|
|
@@ -132,10 +132,10 @@ jobs:
|
|
|
132
132
|
runs-on: ubuntu-latest
|
|
133
133
|
|
|
134
134
|
steps:
|
|
135
|
-
- uses: actions/checkout@
|
|
135
|
+
- uses: actions/checkout@v5
|
|
136
136
|
|
|
137
137
|
- name: Download all artifacts
|
|
138
|
-
uses: actions/download-artifact@
|
|
138
|
+
uses: actions/download-artifact@v8
|
|
139
139
|
with:
|
|
140
140
|
path: artifacts
|
|
141
141
|
|
|
@@ -149,7 +149,7 @@ jobs:
|
|
|
149
149
|
fi
|
|
150
150
|
|
|
151
151
|
- name: Create Release
|
|
152
|
-
uses: softprops/action-gh-release@
|
|
152
|
+
uses: softprops/action-gh-release@v3
|
|
153
153
|
with:
|
|
154
154
|
name: bt-cli v${{ steps.version.outputs.version }}
|
|
155
155
|
tag_name: v${{ steps.version.outputs.version }}
|
|
@@ -245,7 +245,7 @@ jobs:
|
|
|
245
245
|
|
|
246
246
|
steps:
|
|
247
247
|
- name: Download package artifact
|
|
248
|
-
uses: actions/download-artifact@
|
|
248
|
+
uses: actions/download-artifact@v8
|
|
249
249
|
with:
|
|
250
250
|
name: python-package
|
|
251
251
|
path: dist
|
|
@@ -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.55**
|
|
4
4
|
|
|
5
5
|
## Setup
|
|
6
6
|
|
|
@@ -32,6 +32,7 @@ Use these slash commands for detailed product guidance:
|
|
|
32
32
|
| `/epmw` | EPM Windows - computers, policies, requests |
|
|
33
33
|
| `/epml` | EPM Linux - RBP roles/cmdgrps/usergrps, policy, test suites, transactions |
|
|
34
34
|
| `/secrets` | Secrets API - folders, static, dynamic AWS credentials, leases, integrations |
|
|
35
|
+
| `/pf` | Pathfinder platform - whoami, site/product access, PAT/MCP tokens, machines |
|
|
35
36
|
|
|
36
37
|
## Command Structure
|
|
37
38
|
|
|
@@ -44,6 +45,7 @@ Use these slash commands for detailed product guidance:
|
|
|
44
45
|
| EPM Windows | `bt epmw` | `computers`, `groups`, `policies`, `requests`, `quick` |
|
|
45
46
|
| EPM Linux | `bt epml` | `rbp` (cmdgrps/hostgrps/usergrps/tmdategrps/roles/policy/tests/tx), `settings`, `users`, `audit`, `siems`, `quick` |
|
|
46
47
|
| Secrets API | `bt secrets` | `folders`, `static`, `dynamic` (incl. `generate`), `leases`, `integrations` |
|
|
48
|
+
| Pathfinder | `bt pf` | `whoami`, `access`, `pats`, `mcp-tokens`, `machines` |
|
|
47
49
|
|
|
48
50
|
## Common Patterns
|
|
49
51
|
|
|
@@ -89,6 +91,7 @@ PASSWORD=$(bt pws quick checkout -s server -a admin --raw)
|
|
|
89
91
|
- **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
92
|
- **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
93
|
- **Secrets `generate` mints real AWS STS credentials and is audited** — don't loop it in dev. Use `dynamic list`/`get` + `leases list` for browsing.
|
|
94
|
+
- **PF URL mapping**: Nomine swagger paths `/api/auth/X` → gateway `https://api.beyondtrust.io/site/<site-id>/platform/auth/X`. **PATs are site-bound** (minted on the active portal site; other sites → `401 Access denied for this site`). Org-level endpoints (`auth/Users`, `auth/Auditing`, `auth/SAMLProviders`) are 403 for PAT principals; user onboarding & token create/revoke need cookie auth on app.beyondtrust.io. Details in `src/bt_cli/data/skills/pf/SKILL.md`.
|
|
92
95
|
|
|
93
96
|
## Functional vs Managed Accounts
|
|
94
97
|
|
|
@@ -118,7 +121,8 @@ src/bt_cli/
|
|
|
118
121
|
├── entitle/ # Entitle
|
|
119
122
|
├── epmw/ # EPM Windows
|
|
120
123
|
├── epml/ # EPM Linux (PAT auth, /site/<id>/epm/linux/... URLs)
|
|
121
|
-
|
|
124
|
+
├── secrets/ # BeyondTrust Secrets API (PAT auth, /site/<id>/secrets/... URLs)
|
|
125
|
+
└── pf/ # Pathfinder platform (PAT auth, /site/<id>/platform/auth/... URLs)
|
|
122
126
|
```
|
|
123
127
|
|
|
124
128
|
Each product follows: `client/` (API), `commands/` (CLI), `models/` (Pydantic)
|
|
@@ -95,6 +95,12 @@ def _get_secrets_app() -> typer.Typer:
|
|
|
95
95
|
return secrets_app
|
|
96
96
|
|
|
97
97
|
|
|
98
|
+
def _get_pf_app() -> typer.Typer:
|
|
99
|
+
"""Lazy load Pathfinder platform commands."""
|
|
100
|
+
from .pf.commands import app as pf_app
|
|
101
|
+
return pf_app
|
|
102
|
+
|
|
103
|
+
|
|
98
104
|
def _get_configure_app() -> typer.Typer:
|
|
99
105
|
"""Lazy load configure commands."""
|
|
100
106
|
from .commands.configure import app as configure_app
|
|
@@ -145,6 +151,11 @@ try:
|
|
|
145
151
|
except Exception:
|
|
146
152
|
pass # Secrets module not ready yet
|
|
147
153
|
|
|
154
|
+
try:
|
|
155
|
+
app.add_typer(_get_pf_app(), name="pf", help="Pathfinder platform commands")
|
|
156
|
+
except Exception:
|
|
157
|
+
pass # PF module not ready yet
|
|
158
|
+
|
|
148
159
|
try:
|
|
149
160
|
app.add_typer(_get_configure_app(), name="configure", help="Configure bt-cli settings")
|
|
150
161
|
except Exception:
|
|
@@ -299,9 +310,9 @@ def tree_command(
|
|
|
299
310
|
|
|
300
311
|
if product:
|
|
301
312
|
product = product.lower()
|
|
302
|
-
if product not in ["pws", "pra", "entitle", "epmw", "epml", "secrets", "quick", "configure"]:
|
|
313
|
+
if product not in ["pws", "pra", "entitle", "epmw", "epml", "secrets", "pf", "quick", "configure"]:
|
|
303
314
|
console.print(f"[red]Unknown product: {product}[/red]")
|
|
304
|
-
console.print("Available: pws, pra, entitle, epmw, epml, secrets, quick, configure")
|
|
315
|
+
console.print("Available: pws, pra, entitle, epmw, epml, secrets, pf, quick, configure")
|
|
305
316
|
raise typer.Exit(1)
|
|
306
317
|
|
|
307
318
|
tree = Tree("[bold cyan]bt[/bold cyan]")
|
|
@@ -868,6 +879,11 @@ def whoami(
|
|
|
868
879
|
if secrets_result:
|
|
869
880
|
results.append(secrets_result)
|
|
870
881
|
|
|
882
|
+
# Test Pathfinder platform
|
|
883
|
+
pf_result = _test_pf_connection()
|
|
884
|
+
if pf_result:
|
|
885
|
+
results.append(pf_result)
|
|
886
|
+
|
|
871
887
|
if not results:
|
|
872
888
|
console.print("[yellow]No products configured.[/yellow]")
|
|
873
889
|
console.print("\nTo configure products, set environment variables or run:")
|
|
@@ -1108,6 +1124,39 @@ def _test_secrets_connection() -> Optional[dict]:
|
|
|
1108
1124
|
}
|
|
1109
1125
|
|
|
1110
1126
|
|
|
1127
|
+
def _test_pf_connection() -> Optional[dict]:
|
|
1128
|
+
"""Test Pathfinder platform connection and return status."""
|
|
1129
|
+
try:
|
|
1130
|
+
from .core.config import load_pf_config
|
|
1131
|
+
from .pf.client import get_client
|
|
1132
|
+
|
|
1133
|
+
config = load_pf_config()
|
|
1134
|
+
masked_pat = config.pat[:12] + "..." if len(config.pat) > 12 else "***"
|
|
1135
|
+
result = {
|
|
1136
|
+
"product": "Pathfinder",
|
|
1137
|
+
"url": f"{config.api_url}/site/{config.site_id}/platform",
|
|
1138
|
+
"auth_method": f"PAT ({masked_pat})",
|
|
1139
|
+
"connected": False,
|
|
1140
|
+
}
|
|
1141
|
+
|
|
1142
|
+
with get_client() as client:
|
|
1143
|
+
info = client.get_user_info() or {}
|
|
1144
|
+
result["connected"] = True
|
|
1145
|
+
result["user_info"] = f"{info.get('email', '?')} ({info.get('role', '?')})"
|
|
1146
|
+
|
|
1147
|
+
return result
|
|
1148
|
+
except ValueError:
|
|
1149
|
+
return None
|
|
1150
|
+
except Exception as e:
|
|
1151
|
+
return {
|
|
1152
|
+
"product": "Pathfinder",
|
|
1153
|
+
"url": "",
|
|
1154
|
+
"auth_method": "-",
|
|
1155
|
+
"connected": False,
|
|
1156
|
+
"error": str(e)[:50],
|
|
1157
|
+
}
|
|
1158
|
+
|
|
1159
|
+
|
|
1111
1160
|
def run() -> None:
|
|
1112
1161
|
"""Run the CLI application."""
|
|
1113
1162
|
app()
|
|
@@ -172,6 +172,10 @@ def _configure_interactive(
|
|
|
172
172
|
if "example" in field_info:
|
|
173
173
|
console.print(f" [dim]Example: {field_info['example']}[/dim]")
|
|
174
174
|
|
|
175
|
+
# Show how-to-find-it hint if available
|
|
176
|
+
if "hint" in field_info:
|
|
177
|
+
console.print(f" [dim]{field_info['hint']}[/dim]")
|
|
178
|
+
|
|
175
179
|
# Handle boolean fields
|
|
176
180
|
if isinstance(default, bool):
|
|
177
181
|
value = Confirm.ask(prompt_text, default=default)
|
|
@@ -146,6 +146,33 @@ class SecretsConfig(ProductConfig):
|
|
|
146
146
|
raise ValueError("BT_SECRETS_PAT is required")
|
|
147
147
|
|
|
148
148
|
|
|
149
|
+
@dataclass
|
|
150
|
+
class PFConfig(ProductConfig):
|
|
151
|
+
"""Pathfinder platform (Nomine auth service) configuration.
|
|
152
|
+
|
|
153
|
+
Uses Personal Access Token (PAT) bearer authentication against the
|
|
154
|
+
BeyondTrust public API gateway. URLs are composed as:
|
|
155
|
+
|
|
156
|
+
{api_url}/site/{site_id}/platform/auth/<path>
|
|
157
|
+
|
|
158
|
+
PATs are bound to the site that was active in the Pathfinder portal
|
|
159
|
+
when they were minted — a PAT for one site gets
|
|
160
|
+
`401 Access denied for this site` on every other site's paths.
|
|
161
|
+
"""
|
|
162
|
+
|
|
163
|
+
site_id: str = ""
|
|
164
|
+
pat: str = ""
|
|
165
|
+
|
|
166
|
+
def validate(self) -> None:
|
|
167
|
+
"""Validate configuration."""
|
|
168
|
+
if not self.api_url:
|
|
169
|
+
raise ValueError("BT_PF_API_URL is required")
|
|
170
|
+
if not self.site_id:
|
|
171
|
+
raise ValueError("BT_PF_SITE_ID is required")
|
|
172
|
+
if not self.pat:
|
|
173
|
+
raise ValueError("BT_PF_PAT is required")
|
|
174
|
+
|
|
175
|
+
|
|
149
176
|
@dataclass
|
|
150
177
|
class EPMLConfig(ProductConfig):
|
|
151
178
|
"""EPM Linux configuration.
|
|
@@ -545,6 +572,42 @@ def load_secrets_config(env_file: Optional[str] = None, profile: Optional[str] =
|
|
|
545
572
|
return config
|
|
546
573
|
|
|
547
574
|
|
|
575
|
+
def load_pf_config(env_file: Optional[str] = None, profile: Optional[str] = None) -> PFConfig:
|
|
576
|
+
"""Load Pathfinder platform configuration.
|
|
577
|
+
|
|
578
|
+
Configuration sources (in order of precedence):
|
|
579
|
+
1. Environment variables
|
|
580
|
+
2. Config file (~/.bt-cli/config.yaml)
|
|
581
|
+
|
|
582
|
+
Environment variables:
|
|
583
|
+
BT_PF_API_URL - Gateway URL (default: https://api.beyondtrust.io)
|
|
584
|
+
BT_PF_SITE_ID - Site UUID the PAT was minted on (required)
|
|
585
|
+
BT_PF_PAT - Personal Access Token (required)
|
|
586
|
+
BT_PF_VERIFY_SSL - SSL verification (default: true)
|
|
587
|
+
BT_PF_TIMEOUT - Request timeout in seconds (default: 30)
|
|
588
|
+
"""
|
|
589
|
+
if env_file:
|
|
590
|
+
load_dotenv(env_file)
|
|
591
|
+
else:
|
|
592
|
+
load_dotenv()
|
|
593
|
+
|
|
594
|
+
profile = profile or _get_profile()
|
|
595
|
+
layered = get_layered_config("pf", profile)
|
|
596
|
+
|
|
597
|
+
if "pat" in layered:
|
|
598
|
+
layered["pat"] = _resolve_value(layered["pat"])
|
|
599
|
+
|
|
600
|
+
config = PFConfig(
|
|
601
|
+
api_url=_strip_controls(layered.get("api_url") or os.getenv("BT_PF_API_URL", "https://api.beyondtrust.io")),
|
|
602
|
+
site_id=_strip_controls(layered.get("site_id") or os.getenv("BT_PF_SITE_ID", "")),
|
|
603
|
+
pat=_strip_controls(layered.get("pat") or os.getenv("BT_PF_PAT", "")),
|
|
604
|
+
verify_ssl=_to_bool(layered.get("verify_ssl")) if "verify_ssl" in layered else _get_bool(os.getenv("BT_PF_VERIFY_SSL")),
|
|
605
|
+
timeout=_to_float(layered.get("timeout")) if "timeout" in layered else _get_float(os.getenv("BT_PF_TIMEOUT"), 30.0),
|
|
606
|
+
)
|
|
607
|
+
config.validate()
|
|
608
|
+
return config
|
|
609
|
+
|
|
610
|
+
|
|
548
611
|
def load_config(product: str, env_file: Optional[str] = None) -> ProductConfig:
|
|
549
612
|
"""Load configuration for a specific product.
|
|
550
613
|
|
|
@@ -565,6 +628,7 @@ def load_config(product: str, env_file: Optional[str] = None) -> ProductConfig:
|
|
|
565
628
|
"epmw": load_epmw_config,
|
|
566
629
|
"epml": load_epml_config,
|
|
567
630
|
"secrets": load_secrets_config,
|
|
631
|
+
"pf": load_pf_config,
|
|
568
632
|
}
|
|
569
633
|
|
|
570
634
|
loader = loaders.get(product.lower())
|
|
@@ -112,6 +112,7 @@ PRODUCTS = {
|
|
|
112
112
|
"required": True,
|
|
113
113
|
"secret": False,
|
|
114
114
|
"example": "7f735e1b-5ecb-49fa-87e8-eddf54a9c745",
|
|
115
|
+
"hint": "Find yours: log into app.beyondtrust.io, choose your site, then browse to https://app.beyondtrust.io/api/platform/currentSite",
|
|
115
116
|
},
|
|
116
117
|
"pat": {"prompt": "Personal Access Token", "required": True, "secret": True},
|
|
117
118
|
"api_version": {
|
|
@@ -124,6 +125,28 @@ PRODUCTS = {
|
|
|
124
125
|
"timeout": {"prompt": "Timeout (seconds)", "required": False, "secret": False, "default": 30},
|
|
125
126
|
},
|
|
126
127
|
},
|
|
128
|
+
"pf": {
|
|
129
|
+
"name": "Pathfinder Platform",
|
|
130
|
+
"fields": {
|
|
131
|
+
"api_url": {
|
|
132
|
+
"prompt": "Gateway URL",
|
|
133
|
+
"required": True,
|
|
134
|
+
"secret": False,
|
|
135
|
+
"default": "https://api.beyondtrust.io",
|
|
136
|
+
"example": "https://api.beyondtrust.io",
|
|
137
|
+
},
|
|
138
|
+
"site_id": {
|
|
139
|
+
"prompt": "Site ID (UUID — the site the PAT was minted on)",
|
|
140
|
+
"required": True,
|
|
141
|
+
"secret": False,
|
|
142
|
+
"example": "6a7546a7-e111-4fe3-bc14-d6de9c2177b9",
|
|
143
|
+
"hint": "Find yours: log into app.beyondtrust.io, choose your site, then browse to https://app.beyondtrust.io/api/platform/currentSite",
|
|
144
|
+
},
|
|
145
|
+
"pat": {"prompt": "Personal Access Token", "required": True, "secret": True},
|
|
146
|
+
"verify_ssl": {"prompt": "Verify SSL", "required": False, "secret": False, "default": True},
|
|
147
|
+
"timeout": {"prompt": "Timeout (seconds)", "required": False, "secret": False, "default": 30},
|
|
148
|
+
},
|
|
149
|
+
},
|
|
127
150
|
"epml": {
|
|
128
151
|
"name": "EPM Linux",
|
|
129
152
|
"fields": {
|
|
@@ -139,6 +162,7 @@ PRODUCTS = {
|
|
|
139
162
|
"required": True,
|
|
140
163
|
"secret": False,
|
|
141
164
|
"example": "c882e07c-753b-4fcd-a1e6-7d39defec5ae",
|
|
165
|
+
"hint": "Find yours: log into app.beyondtrust.io, choose your site, then browse to https://app.beyondtrust.io/api/platform/currentSite",
|
|
142
166
|
},
|
|
143
167
|
"pat": {"prompt": "Personal Access Token", "required": True, "secret": True},
|
|
144
168
|
"default_host_id": {
|
|
@@ -361,6 +385,7 @@ def _get_env_prefix(product: str) -> str:
|
|
|
361
385
|
"epmw": "BT_EPM",
|
|
362
386
|
"epml": "BT_EPML",
|
|
363
387
|
"secrets": "BT_SECRETS",
|
|
388
|
+
"pf": "BT_PF",
|
|
364
389
|
}
|
|
365
390
|
return prefixes.get(product, f"BT_{product.upper()}")
|
|
366
391
|
|
|
@@ -399,6 +424,12 @@ def _get_env_mappings(product: str) -> dict[str, str]:
|
|
|
399
424
|
# Secrets API doesn't use api_key/client_id/client_secret — drop the inherited mappings
|
|
400
425
|
for unused in ("api_key", "client_id", "client_secret"):
|
|
401
426
|
mappings.pop(unused, None)
|
|
427
|
+
elif product == "pf":
|
|
428
|
+
mappings["site_id"] = f"{prefix}_SITE_ID"
|
|
429
|
+
mappings["pat"] = f"{prefix}_PAT"
|
|
430
|
+
# Pathfinder uses PAT auth only — drop the inherited mappings
|
|
431
|
+
for unused in ("api_key", "client_id", "client_secret"):
|
|
432
|
+
mappings.pop(unused, None)
|
|
402
433
|
|
|
403
434
|
return mappings
|
|
404
435
|
|
|
@@ -98,8 +98,20 @@ bt epml iolog list -H 200 --start 0 --len 200
|
|
|
98
98
|
bt epml client-pkg list # signed S3 URLs (~30min TTL)
|
|
99
99
|
bt epml client-pkg status # build queue
|
|
100
100
|
bt epml client-pkg link epml-client.x86_64.rpm
|
|
101
|
+
bt epml client-pkg build --wait # trigger build, poll to done (~15min max)
|
|
102
|
+
bt epml client-pkg download --all -d ./pkgs # fetch via pre-signed link (NO auth header)
|
|
103
|
+
bt epml client-pkg download epml-client.x86_64.rpm
|
|
104
|
+
bt epml client-pkg install-token --expiry 480 # agent registration JWT (pbactivate -t <token>)
|
|
105
|
+
TOKEN=$(bt epml client-pkg install-token --raw) # scripting
|
|
106
|
+
bt epml quick agent-setup -d ./pkgs --expiry 480 # list→build-if-empty→download-all→token
|
|
101
107
|
```
|
|
102
108
|
|
|
109
|
+
**Agent onboarding notes:** installation token expiry is minutes, 30–525600 (one
|
|
110
|
+
year); the token is a JWT consumed by `pbactivate -t <token>` on the endpoint.
|
|
111
|
+
Package download links are pre-signed S3 URLs that expire ~30 min after listing
|
|
112
|
+
and REJECT requests carrying an `Authorization` header — the CLI handles both
|
|
113
|
+
(fresh listing + headerless download).
|
|
114
|
+
|
|
103
115
|
## RBP — Role-Based Policy
|
|
104
116
|
|
|
105
117
|
All under `/api/pbul/{hostid}/rbp/...` (default host = `BT_EPML_DEFAULT_HOST`).
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: pf
|
|
3
|
+
description: Pathfinder platform commands — whoami, site/product access, PAT/MCP token listing, machine registry. Use when working with the Pathfinder portal API (Nomine), platform PATs, site IDs, or registered machine identities.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Pathfinder Platform (`bt pf`)
|
|
7
|
+
|
|
8
|
+
The Pathfinder platform API (internal name: **Nomine**) manages the BeyondTrust
|
|
9
|
+
cloud portal layer: users, sites, product access, tokens, and machine
|
|
10
|
+
identities. `bt pf` talks to it through the public API gateway with PAT auth —
|
|
11
|
+
the same pattern as `bt secrets` / `bt epml`.
|
|
12
|
+
|
|
13
|
+
## Configuration
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
export BT_PF_API_URL=https://api.beyondtrust.io # default
|
|
17
|
+
export BT_PF_SITE_ID=<site-uuid> # required — the site the PAT was minted on
|
|
18
|
+
export BT_PF_PAT=PAT_xxx # required (mint at app.beyondtrust.io → Manage Profile)
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Site ID discovery: log into app.beyondtrust.io, choose your site, then browse
|
|
22
|
+
to `https://app.beyondtrust.io/api/platform/currentSite`.
|
|
23
|
+
|
|
24
|
+
## URL mapping (the thing everyone gets wrong)
|
|
25
|
+
|
|
26
|
+
The Nomine swagger writes paths as `/api/auth/X`. On the gateway the product
|
|
27
|
+
segment is `platform` and the `auth/` prefix stays:
|
|
28
|
+
|
|
29
|
+
spec /api/auth/UserInfo → https://api.beyondtrust.io/site/<site-id>/platform/auth/UserInfo
|
|
30
|
+
|
|
31
|
+
**PATs are site-bound**: minted while a site is active in the portal, valid
|
|
32
|
+
only for that site's `/site/<id>/...` paths. Any other site → `401 Access
|
|
33
|
+
denied for this site`. A just-minted PAT can take ~1s to propagate
|
|
34
|
+
(transient `401 Personal access token not found` — retry once).
|
|
35
|
+
|
|
36
|
+
## Commands
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
bt pf auth test # connectivity + PAT validity
|
|
40
|
+
bt pf whoami # UserInfo for the PAT owner
|
|
41
|
+
bt pf access # site + product access rules (also -o json)
|
|
42
|
+
bt pf pats list # PATs (values masked; --show-tokens to reveal)
|
|
43
|
+
bt pf mcp-tokens list # MCP tokens (same masking)
|
|
44
|
+
bt pf machines list # registered machine IDs
|
|
45
|
+
bt pf machines get <uuid> # one machine's record (secret hash never shown)
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## What works over PAT vs what doesn't (verified 2026-06-11)
|
|
49
|
+
|
|
50
|
+
| Endpoint | PAT result |
|
|
51
|
+
|----------|------------|
|
|
52
|
+
| `auth/UserInfo`, `auth/User/Access`, `auth/User/Token`, `auth/User/McpToken` | 200 — any valid PAT |
|
|
53
|
+
| `auth/Machine`, `auth/Machine/{id}` | 200 — only with a PAT from a product-bearing site (Administration-site PATs get 403) |
|
|
54
|
+
| `auth/Users`, `auth/SAMLProviders`, `auth/Auditing` | 403 — org-level, blocked for PAT principals regardless of scopes seen so far |
|
|
55
|
+
| `auth/UserInvitation` | 405 on GET (POST-only; reachable — onboarding over PAT untested) |
|
|
56
|
+
|
|
57
|
+
PAT `access` scopes are the lowercase product IDs of the mint site (visible in
|
|
58
|
+
`bt pf pats list`). The org-level 403s may require a scope no UI-minted PAT
|
|
59
|
+
carries — unresolved.
|
|
60
|
+
|
|
61
|
+
## Machines
|
|
62
|
+
|
|
63
|
+
Platform machine identities: non-user principals (machineId + machineSecret)
|
|
64
|
+
used by agents/integrations to authenticate. `POST
|
|
65
|
+
login.beyondtrust.io/bt.identity/Machine/AccessToken {machineId,
|
|
66
|
+
machineSecret}` → 30-min JWT (machineEndpoints.* scopes only). Records carry
|
|
67
|
+
an activation-token lifecycle (created/expiration) and `active` flag —
|
|
68
|
+
an entry with `active: false` and a pending activation token was registered
|
|
69
|
+
but never activated.
|
|
70
|
+
|
|
71
|
+
## Not exposed via PAT (cookie-auth only, app.beyondtrust.io/api/auth/*)
|
|
72
|
+
|
|
73
|
+
User onboarding (UserInvitation POST + Accept), PAT/MCP token creation and
|
|
74
|
+
revocation, user listing, auditing, SAML providers. These need the `access`
|
|
75
|
+
session cookie (browser JWT ~5min TTL, or the programmatic
|
|
76
|
+
SignIn → /session/login flow — password-auth users only, not federated).
|
|
@@ -232,6 +232,49 @@ class EPMLClient:
|
|
|
232
232
|
"""GET /api/epml/clientpkg/status — `{building: bool}` build queue status."""
|
|
233
233
|
return self.get("/api/epml/clientpkg/status")
|
|
234
234
|
|
|
235
|
+
def trigger_client_package_build(self) -> Any:
|
|
236
|
+
"""POST /api/epml/clientpkg — queue a fresh client package build.
|
|
237
|
+
|
|
238
|
+
Builds usually finish within a few minutes but can take ~15 during
|
|
239
|
+
busy periods. Poll `get_client_package_status()` until building=false.
|
|
240
|
+
"""
|
|
241
|
+
return self.post("/api/epml/clientpkg")
|
|
242
|
+
|
|
243
|
+
def download_package(self, link: str, dest_path: str) -> int:
|
|
244
|
+
"""Download a client package from its pre-signed S3 link.
|
|
245
|
+
|
|
246
|
+
Pre-signed URLs carry their own authorization and REJECT requests
|
|
247
|
+
that also include an Authorization header — so unlike every other
|
|
248
|
+
call, this one sends no auth. Links expire ~30 minutes after the
|
|
249
|
+
package listing that produced them.
|
|
250
|
+
|
|
251
|
+
Returns:
|
|
252
|
+
Number of bytes written to dest_path.
|
|
253
|
+
"""
|
|
254
|
+
bytes_written = 0
|
|
255
|
+
with self._client.stream("GET", link, follow_redirects=True) as response:
|
|
256
|
+
response.raise_for_status()
|
|
257
|
+
with open(dest_path, "wb") as f:
|
|
258
|
+
for chunk in response.iter_bytes():
|
|
259
|
+
f.write(chunk)
|
|
260
|
+
bytes_written += len(chunk)
|
|
261
|
+
return bytes_written
|
|
262
|
+
|
|
263
|
+
# ----- Installation tokens (agent registration) -----------------------
|
|
264
|
+
|
|
265
|
+
def get_installation_token(self, expiry: int) -> Dict[str, Any]:
|
|
266
|
+
"""GET /api/btplatform/installationtoken — short-lived agent registration JWT.
|
|
267
|
+
|
|
268
|
+
Args:
|
|
269
|
+
expiry: Token lifetime in minutes, 30 (half an hour) to
|
|
270
|
+
525600 (one year). Required by the API.
|
|
271
|
+
|
|
272
|
+
Returns:
|
|
273
|
+
``{"token": "eyJ..."}`` — passed to ``pbactivate -t <token>``
|
|
274
|
+
on the endpoint being registered.
|
|
275
|
+
"""
|
|
276
|
+
return self.get("/api/btplatform/installationtoken", params={"expiry": expiry})
|
|
277
|
+
|
|
235
278
|
# ----- License clients (installed agents) ----------------------------
|
|
236
279
|
|
|
237
280
|
def list_license_clients(self, host_id: Optional[int] = None) -> Dict[str, Any]:
|