bt-cli 0.4.25__tar.gz → 0.4.26__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.25 → bt_cli-0.4.26}/CLAUDE.md +1 -1
- {bt_cli-0.4.25 → bt_cli-0.4.26}/PKG-INFO +1 -1
- {bt_cli-0.4.25 → bt_cli-0.4.26}/pyproject.toml +1 -1
- {bt_cli-0.4.25 → bt_cli-0.4.26}/src/bt_cli/__init__.py +1 -1
- {bt_cli-0.4.25 → bt_cli-0.4.26}/src/bt_cli/pra/client/base.py +49 -5
- {bt_cli-0.4.25 → bt_cli-0.4.26}/src/bt_cli/pra/commands/jump_items.py +88 -18
- {bt_cli-0.4.25 → bt_cli-0.4.26}/src/bt_cli/pws/client/beyondinsight.py +43 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/src/bt_cli/pws/commands/__init__.py +2 -1
- bt_cli-0.4.26/src/bt_cli/pws/commands/attributes.py +176 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/.claude/skills/bt/SKILL.md +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/.claude/skills/entitle/SKILL.md +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/.claude/skills/epmw/SKILL.md +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/.claude/skills/pra/SKILL.md +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/.claude/skills/pws/SKILL.md +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/.env.example +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/.github/workflows/ci.yml +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/.github/workflows/release.yml +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/.gitignore +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/README.md +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/assets/cli-help.png +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/assets/cli-output.png +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/bt-cli.spec +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/bt_entry.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/scripts/bt_entry.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/scripts/sync-package-data.sh +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/src/bt_cli/cli.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/src/bt_cli/commands/__init__.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/src/bt_cli/commands/configure.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/src/bt_cli/commands/learn.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/src/bt_cli/commands/quick.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/src/bt_cli/core/__init__.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/src/bt_cli/core/auth.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/src/bt_cli/core/client.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/src/bt_cli/core/config.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/src/bt_cli/core/config_file.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/src/bt_cli/core/csv_utils.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/src/bt_cli/core/errors.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/src/bt_cli/core/output.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/src/bt_cli/core/prompts.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/src/bt_cli/core/rest_debug.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/src/bt_cli/data/CLAUDE.md +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/src/bt_cli/data/__init__.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/src/bt_cli/data/skills/bt/SKILL.md +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/src/bt_cli/data/skills/entitle/SKILL.md +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/src/bt_cli/data/skills/epmw/SKILL.md +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/src/bt_cli/data/skills/pra/SKILL.md +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/src/bt_cli/data/skills/pws/SKILL.md +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/src/bt_cli/entitle/__init__.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/src/bt_cli/entitle/client/__init__.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/src/bt_cli/entitle/client/base.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/src/bt_cli/entitle/commands/__init__.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/src/bt_cli/entitle/commands/accounts.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/src/bt_cli/entitle/commands/applications.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/src/bt_cli/entitle/commands/auth.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/src/bt_cli/entitle/commands/bundles.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/src/bt_cli/entitle/commands/integrations.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/src/bt_cli/entitle/commands/permissions.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/src/bt_cli/entitle/commands/policies.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/src/bt_cli/entitle/commands/resources.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/src/bt_cli/entitle/commands/roles.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/src/bt_cli/entitle/commands/users.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/src/bt_cli/entitle/commands/workflows.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/src/bt_cli/entitle/models/__init__.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/src/bt_cli/entitle/models/bundle.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/src/bt_cli/entitle/models/common.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/src/bt_cli/entitle/models/integration.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/src/bt_cli/entitle/models/permission.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/src/bt_cli/entitle/models/policy.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/src/bt_cli/entitle/models/resource.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/src/bt_cli/entitle/models/role.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/src/bt_cli/entitle/models/user.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/src/bt_cli/entitle/models/workflow.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/src/bt_cli/epmw/__init__.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/src/bt_cli/epmw/client/__init__.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/src/bt_cli/epmw/client/base.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/src/bt_cli/epmw/commands/__init__.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/src/bt_cli/epmw/commands/audits.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/src/bt_cli/epmw/commands/auth.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/src/bt_cli/epmw/commands/computers.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/src/bt_cli/epmw/commands/events.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/src/bt_cli/epmw/commands/groups.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/src/bt_cli/epmw/commands/policies.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/src/bt_cli/epmw/commands/quick.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/src/bt_cli/epmw/commands/requests.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/src/bt_cli/epmw/commands/roles.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/src/bt_cli/epmw/commands/tasks.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/src/bt_cli/epmw/commands/users.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/src/bt_cli/epmw/models/__init__.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/src/bt_cli/pra/__init__.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/src/bt_cli/pra/client/__init__.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/src/bt_cli/pra/commands/__init__.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/src/bt_cli/pra/commands/auth.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/src/bt_cli/pra/commands/import_export.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/src/bt_cli/pra/commands/jump_clients.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/src/bt_cli/pra/commands/jump_groups.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/src/bt_cli/pra/commands/jumpoints.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/src/bt_cli/pra/commands/policies.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/src/bt_cli/pra/commands/quick.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/src/bt_cli/pra/commands/teams.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/src/bt_cli/pra/commands/users.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/src/bt_cli/pra/commands/vault.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/src/bt_cli/pra/models/__init__.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/src/bt_cli/pra/models/common.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/src/bt_cli/pra/models/jump_client.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/src/bt_cli/pra/models/jump_group.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/src/bt_cli/pra/models/jump_item.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/src/bt_cli/pra/models/jumpoint.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/src/bt_cli/pra/models/team.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/src/bt_cli/pra/models/user.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/src/bt_cli/pra/models/vault.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/src/bt_cli/pws/__init__.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/src/bt_cli/pws/client/__init__.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/src/bt_cli/pws/client/base.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/src/bt_cli/pws/client/passwordsafe.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/src/bt_cli/pws/commands/accounts.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/src/bt_cli/pws/commands/assets.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/src/bt_cli/pws/commands/auth.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/src/bt_cli/pws/commands/clouds.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/src/bt_cli/pws/commands/config.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/src/bt_cli/pws/commands/credentials.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/src/bt_cli/pws/commands/databases.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/src/bt_cli/pws/commands/directories.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/src/bt_cli/pws/commands/functional.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/src/bt_cli/pws/commands/import_export.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/src/bt_cli/pws/commands/platforms.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/src/bt_cli/pws/commands/quick.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/src/bt_cli/pws/commands/search.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/src/bt_cli/pws/commands/secrets.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/src/bt_cli/pws/commands/systems.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/src/bt_cli/pws/commands/users.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/src/bt_cli/pws/commands/workgroups.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/src/bt_cli/pws/config.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/src/bt_cli/pws/models/__init__.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/src/bt_cli/pws/models/account.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/src/bt_cli/pws/models/asset.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/src/bt_cli/pws/models/common.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/src/bt_cli/pws/models/system.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/tests/__init__.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/tests/conftest.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/tests/core/__init__.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/tests/core/test_auth.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/tests/core/test_config.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/tests/core/test_errors.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/tests/core/test_rest_debug.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/tests/entitle/__init__.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/tests/entitle/test_client.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/tests/entitle/test_commands.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/tests/entitle-smoke-test.sh +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/tests/epmw/__init__.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/tests/epmw/test_client.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/tests/epmw/test_commands.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/tests/epmw-quick-test-plan.md +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/tests/fixtures/__init__.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/tests/fixtures/responses.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/tests/integration/__init__.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/tests/integration/conftest.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/tests/integration/helpers.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/tests/integration/test_entitle_integration.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/tests/integration/test_epmw_integration.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/tests/integration/test_epmw_lifecycle.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/tests/integration/test_pra_integration.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/tests/integration/test_pra_lifecycle.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/tests/integration/test_pws_integration.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/tests/integration/test_pws_lifecycle.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/tests/pra/__init__.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/tests/pra/test_client.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/tests/pra/test_commands.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/tests/pra-smoke-test.sh +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/tests/pra-test-plan.md +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/tests/pws/__init__.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/tests/pws/test_client.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/tests/pws/test_commands.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/tests/pws-quick-test-plan.md +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.26}/tests/pws-smoke-test.sh +0 -0
|
@@ -278,13 +278,22 @@ class PRAClient:
|
|
|
278
278
|
self,
|
|
279
279
|
jump_group_id: Optional[int] = None,
|
|
280
280
|
jumpoint_id: Optional[int] = None,
|
|
281
|
+
tag: Optional[str] = None,
|
|
282
|
+
name: Optional[str] = None,
|
|
283
|
+
hostname: Optional[str] = None,
|
|
281
284
|
) -> List[Dict[str, Any]]:
|
|
282
|
-
"""List Shell Jump items."""
|
|
285
|
+
"""List Shell Jump items with optional filters."""
|
|
283
286
|
params = {}
|
|
284
287
|
if jump_group_id:
|
|
285
288
|
params["jump_group_id"] = jump_group_id
|
|
286
289
|
if jumpoint_id:
|
|
287
290
|
params["jumpoint_id"] = jumpoint_id
|
|
291
|
+
if tag:
|
|
292
|
+
params["tag"] = tag
|
|
293
|
+
if name:
|
|
294
|
+
params["name"] = name
|
|
295
|
+
if hostname:
|
|
296
|
+
params["hostname"] = hostname
|
|
288
297
|
return self.get_paginated("/jump-item/shell-jump", params)
|
|
289
298
|
|
|
290
299
|
def get_shell_jump(self, item_id: int) -> Dict[str, Any]:
|
|
@@ -375,13 +384,22 @@ class PRAClient:
|
|
|
375
384
|
self,
|
|
376
385
|
jump_group_id: Optional[int] = None,
|
|
377
386
|
jumpoint_id: Optional[int] = None,
|
|
387
|
+
tag: Optional[str] = None,
|
|
388
|
+
name: Optional[str] = None,
|
|
389
|
+
hostname: Optional[str] = None,
|
|
378
390
|
) -> List[Dict[str, Any]]:
|
|
379
|
-
"""List Remote RDP Jump items."""
|
|
391
|
+
"""List Remote RDP Jump items with optional filters."""
|
|
380
392
|
params = {}
|
|
381
393
|
if jump_group_id:
|
|
382
394
|
params["jump_group_id"] = jump_group_id
|
|
383
395
|
if jumpoint_id:
|
|
384
396
|
params["jumpoint_id"] = jumpoint_id
|
|
397
|
+
if tag:
|
|
398
|
+
params["tag"] = tag
|
|
399
|
+
if name:
|
|
400
|
+
params["name"] = name
|
|
401
|
+
if hostname:
|
|
402
|
+
params["hostname"] = hostname
|
|
385
403
|
return self.get_paginated("/jump-item/remote-rdp", params)
|
|
386
404
|
|
|
387
405
|
def get_rdp_jump(self, item_id: int) -> Dict[str, Any]:
|
|
@@ -423,21 +441,36 @@ class PRAClient:
|
|
|
423
441
|
def list_vnc_jumps(
|
|
424
442
|
self,
|
|
425
443
|
jump_group_id: Optional[int] = None,
|
|
444
|
+
tag: Optional[str] = None,
|
|
445
|
+
name: Optional[str] = None,
|
|
446
|
+
hostname: Optional[str] = None,
|
|
426
447
|
) -> List[Dict[str, Any]]:
|
|
427
|
-
"""List Remote VNC Jump items."""
|
|
448
|
+
"""List Remote VNC Jump items with optional filters."""
|
|
428
449
|
params = {}
|
|
429
450
|
if jump_group_id:
|
|
430
451
|
params["jump_group_id"] = jump_group_id
|
|
452
|
+
if tag:
|
|
453
|
+
params["tag"] = tag
|
|
454
|
+
if name:
|
|
455
|
+
params["name"] = name
|
|
456
|
+
if hostname:
|
|
457
|
+
params["hostname"] = hostname
|
|
431
458
|
return self.get_paginated("/jump-item/remote-vnc", params)
|
|
432
459
|
|
|
433
460
|
def list_web_jumps(
|
|
434
461
|
self,
|
|
435
462
|
jump_group_id: Optional[int] = None,
|
|
463
|
+
tag: Optional[str] = None,
|
|
464
|
+
name: Optional[str] = None,
|
|
436
465
|
) -> List[Dict[str, Any]]:
|
|
437
|
-
"""List Web Jump items."""
|
|
466
|
+
"""List Web Jump items with optional filters."""
|
|
438
467
|
params = {}
|
|
439
468
|
if jump_group_id:
|
|
440
469
|
params["jump_group_id"] = jump_group_id
|
|
470
|
+
if tag:
|
|
471
|
+
params["tag"] = tag
|
|
472
|
+
if name:
|
|
473
|
+
params["name"] = name
|
|
441
474
|
return self.get_paginated("/jump-item/web-jump", params)
|
|
442
475
|
|
|
443
476
|
def get_web_jump(self, item_id: int) -> Dict[str, Any]:
|
|
@@ -452,11 +485,22 @@ class PRAClient:
|
|
|
452
485
|
self,
|
|
453
486
|
jump_group_id: Optional[int] = None,
|
|
454
487
|
tunnel_type: Optional[str] = None,
|
|
488
|
+
tag: Optional[str] = None,
|
|
489
|
+
name: Optional[str] = None,
|
|
490
|
+
hostname: Optional[str] = None,
|
|
455
491
|
) -> List[Dict[str, Any]]:
|
|
456
|
-
"""List Protocol Tunnel Jump items (TCP, MSSQL, K8s)."""
|
|
492
|
+
"""List Protocol Tunnel Jump items (TCP, MSSQL, K8s) with optional filters."""
|
|
457
493
|
params = {}
|
|
458
494
|
if jump_group_id:
|
|
459
495
|
params["jump_group_id"] = jump_group_id
|
|
496
|
+
if tunnel_type:
|
|
497
|
+
params["tunnel_type"] = tunnel_type
|
|
498
|
+
if tag:
|
|
499
|
+
params["tag"] = tag
|
|
500
|
+
if name:
|
|
501
|
+
params["name"] = name
|
|
502
|
+
if hostname:
|
|
503
|
+
params["hostname"] = hostname
|
|
460
504
|
return self.get_paginated("/jump-item/protocol-tunnel-jump", params)
|
|
461
505
|
|
|
462
506
|
def get_protocol_tunnel(self, item_id: int) -> Dict[str, Any]:
|
|
@@ -18,6 +18,9 @@ app.add_typer(shell_app, name="shell")
|
|
|
18
18
|
def list_shell_jumps(
|
|
19
19
|
jump_group_id: Optional[int] = typer.Option(None, "--jump-group", "-g", help="Filter by Jump Group"),
|
|
20
20
|
jumpoint_id: Optional[int] = typer.Option(None, "--jumpoint", "-j", help="Filter by Jumpoint"),
|
|
21
|
+
tag: Optional[str] = typer.Option(None, "--tag", "-t", help="Filter by tag (exact match)"),
|
|
22
|
+
name: Optional[str] = typer.Option(None, "--name", "-n", help="Filter by name (exact match)"),
|
|
23
|
+
hostname: Optional[str] = typer.Option(None, "--hostname", help="Filter by hostname (exact match)"),
|
|
21
24
|
output: OutputFormat = typer.Option(OutputFormat.TABLE, "--output", "-o"),
|
|
22
25
|
):
|
|
23
26
|
"""List Shell Jump items."""
|
|
@@ -25,7 +28,13 @@ def list_shell_jumps(
|
|
|
25
28
|
|
|
26
29
|
try:
|
|
27
30
|
client = get_client()
|
|
28
|
-
items = client.list_shell_jumps(
|
|
31
|
+
items = client.list_shell_jumps(
|
|
32
|
+
jump_group_id=jump_group_id,
|
|
33
|
+
jumpoint_id=jumpoint_id,
|
|
34
|
+
tag=tag,
|
|
35
|
+
name=name,
|
|
36
|
+
hostname=hostname,
|
|
37
|
+
)
|
|
29
38
|
|
|
30
39
|
if output == OutputFormat.JSON:
|
|
31
40
|
print_json(items)
|
|
@@ -199,15 +208,23 @@ def update_shell_jump(
|
|
|
199
208
|
def delete_shell_jump(
|
|
200
209
|
item_id: int = typer.Argument(..., help="Shell Jump ID"),
|
|
201
210
|
force: bool = typer.Option(False, "--force", "-f", help="Skip confirmation"),
|
|
211
|
+
dry_run: bool = typer.Option(False, "--dry-run", help="Show what would be deleted without deleting"),
|
|
202
212
|
):
|
|
203
213
|
"""Delete a Shell Jump item."""
|
|
204
214
|
from bt_cli.pra.client import get_client
|
|
205
215
|
|
|
206
|
-
if not force:
|
|
207
|
-
typer.confirm(f"Delete shell jump {item_id}?", abort=True)
|
|
208
|
-
|
|
209
216
|
try:
|
|
210
217
|
client = get_client()
|
|
218
|
+
# Fetch item details for display
|
|
219
|
+
if dry_run:
|
|
220
|
+
item = client.get_shell_jump(item_id)
|
|
221
|
+
print_success(f"[DRY-RUN] Would delete shell jump: {item.get('name')} (ID: {item_id})")
|
|
222
|
+
return
|
|
223
|
+
|
|
224
|
+
if not force:
|
|
225
|
+
item = client.get_shell_jump(item_id)
|
|
226
|
+
typer.confirm(f"Delete shell jump '{item.get('name')}' (ID: {item_id})?", abort=True)
|
|
227
|
+
|
|
211
228
|
client.delete_shell_jump(item_id)
|
|
212
229
|
print_success(f"Deleted shell jump {item_id}")
|
|
213
230
|
except httpx.HTTPStatusError as e:
|
|
@@ -230,6 +247,9 @@ app.add_typer(rdp_app, name="rdp")
|
|
|
230
247
|
def list_rdp_jumps(
|
|
231
248
|
jump_group_id: Optional[int] = typer.Option(None, "--jump-group", "-g", help="Filter by Jump Group"),
|
|
232
249
|
jumpoint_id: Optional[int] = typer.Option(None, "--jumpoint", "-j", help="Filter by Jumpoint"),
|
|
250
|
+
tag: Optional[str] = typer.Option(None, "--tag", "-t", help="Filter by tag (exact match)"),
|
|
251
|
+
name: Optional[str] = typer.Option(None, "--name", "-n", help="Filter by name (exact match)"),
|
|
252
|
+
hostname: Optional[str] = typer.Option(None, "--hostname", help="Filter by hostname (exact match)"),
|
|
233
253
|
output: OutputFormat = typer.Option(OutputFormat.TABLE, "--output", "-o"),
|
|
234
254
|
):
|
|
235
255
|
"""List Remote RDP Jump items."""
|
|
@@ -237,7 +257,13 @@ def list_rdp_jumps(
|
|
|
237
257
|
|
|
238
258
|
try:
|
|
239
259
|
client = get_client()
|
|
240
|
-
items = client.list_rdp_jumps(
|
|
260
|
+
items = client.list_rdp_jumps(
|
|
261
|
+
jump_group_id=jump_group_id,
|
|
262
|
+
jumpoint_id=jumpoint_id,
|
|
263
|
+
tag=tag,
|
|
264
|
+
name=name,
|
|
265
|
+
hostname=hostname,
|
|
266
|
+
)
|
|
241
267
|
|
|
242
268
|
if output == OutputFormat.JSON:
|
|
243
269
|
print_json(items)
|
|
@@ -358,15 +384,22 @@ def create_rdp_jump(
|
|
|
358
384
|
def delete_rdp_jump(
|
|
359
385
|
item_id: int = typer.Argument(..., help="RDP Jump ID"),
|
|
360
386
|
force: bool = typer.Option(False, "--force", "-f", help="Skip confirmation"),
|
|
387
|
+
dry_run: bool = typer.Option(False, "--dry-run", help="Show what would be deleted without deleting"),
|
|
361
388
|
):
|
|
362
389
|
"""Delete a Remote RDP Jump item."""
|
|
363
390
|
from bt_cli.pra.client import get_client
|
|
364
391
|
|
|
365
|
-
if not force:
|
|
366
|
-
typer.confirm(f"Delete RDP jump {item_id}?", abort=True)
|
|
367
|
-
|
|
368
392
|
try:
|
|
369
393
|
client = get_client()
|
|
394
|
+
if dry_run:
|
|
395
|
+
item = client.get_rdp_jump(item_id)
|
|
396
|
+
print_success(f"[DRY-RUN] Would delete RDP jump: {item.get('name')} (ID: {item_id})")
|
|
397
|
+
return
|
|
398
|
+
|
|
399
|
+
if not force:
|
|
400
|
+
item = client.get_rdp_jump(item_id)
|
|
401
|
+
typer.confirm(f"Delete RDP jump '{item.get('name')}' (ID: {item_id})?", abort=True)
|
|
402
|
+
|
|
370
403
|
client.delete_rdp_jump(item_id)
|
|
371
404
|
print_success(f"Deleted RDP jump {item_id}")
|
|
372
405
|
except httpx.HTTPStatusError as e:
|
|
@@ -388,6 +421,9 @@ app.add_typer(vnc_app, name="vnc")
|
|
|
388
421
|
@vnc_app.command("list")
|
|
389
422
|
def list_vnc_jumps(
|
|
390
423
|
jump_group_id: Optional[int] = typer.Option(None, "--jump-group", "-g", help="Filter by Jump Group"),
|
|
424
|
+
tag: Optional[str] = typer.Option(None, "--tag", "-t", help="Filter by tag (exact match)"),
|
|
425
|
+
name: Optional[str] = typer.Option(None, "--name", "-n", help="Filter by name (exact match)"),
|
|
426
|
+
hostname: Optional[str] = typer.Option(None, "--hostname", help="Filter by hostname (exact match)"),
|
|
391
427
|
output: OutputFormat = typer.Option(OutputFormat.TABLE, "--output", "-o"),
|
|
392
428
|
):
|
|
393
429
|
"""List Remote VNC Jump items."""
|
|
@@ -395,7 +431,12 @@ def list_vnc_jumps(
|
|
|
395
431
|
|
|
396
432
|
try:
|
|
397
433
|
client = get_client()
|
|
398
|
-
items = client.list_vnc_jumps(
|
|
434
|
+
items = client.list_vnc_jumps(
|
|
435
|
+
jump_group_id=jump_group_id,
|
|
436
|
+
tag=tag,
|
|
437
|
+
name=name,
|
|
438
|
+
hostname=hostname,
|
|
439
|
+
)
|
|
399
440
|
|
|
400
441
|
if output == OutputFormat.JSON:
|
|
401
442
|
print_json(items)
|
|
@@ -427,6 +468,8 @@ app.add_typer(web_app, name="web")
|
|
|
427
468
|
@web_app.command("list")
|
|
428
469
|
def list_web_jumps(
|
|
429
470
|
jump_group_id: Optional[int] = typer.Option(None, "--jump-group", "-g", help="Filter by Jump Group"),
|
|
471
|
+
tag: Optional[str] = typer.Option(None, "--tag", "-t", help="Filter by tag (exact match)"),
|
|
472
|
+
name: Optional[str] = typer.Option(None, "--name", "-n", help="Filter by name (exact match)"),
|
|
430
473
|
output: OutputFormat = typer.Option(OutputFormat.TABLE, "--output", "-o"),
|
|
431
474
|
):
|
|
432
475
|
"""List Web Jump items."""
|
|
@@ -434,7 +477,11 @@ def list_web_jumps(
|
|
|
434
477
|
|
|
435
478
|
try:
|
|
436
479
|
client = get_client()
|
|
437
|
-
items = client.list_web_jumps(
|
|
480
|
+
items = client.list_web_jumps(
|
|
481
|
+
jump_group_id=jump_group_id,
|
|
482
|
+
tag=tag,
|
|
483
|
+
name=name,
|
|
484
|
+
)
|
|
438
485
|
|
|
439
486
|
if output == OutputFormat.JSON:
|
|
440
487
|
print_json(items)
|
|
@@ -500,15 +547,22 @@ def get_web_jump(
|
|
|
500
547
|
def delete_web_jump(
|
|
501
548
|
item_id: int = typer.Argument(..., help="Web Jump ID"),
|
|
502
549
|
force: bool = typer.Option(False, "--force", "-f", help="Skip confirmation"),
|
|
550
|
+
dry_run: bool = typer.Option(False, "--dry-run", help="Show what would be deleted without deleting"),
|
|
503
551
|
):
|
|
504
552
|
"""Delete a Web Jump item."""
|
|
505
553
|
from bt_cli.pra.client import get_client
|
|
506
554
|
|
|
507
|
-
if not force:
|
|
508
|
-
typer.confirm(f"Delete web jump {item_id}?", abort=True)
|
|
509
|
-
|
|
510
555
|
try:
|
|
511
556
|
client = get_client()
|
|
557
|
+
if dry_run:
|
|
558
|
+
item = client.get_web_jump(item_id)
|
|
559
|
+
print_success(f"[DRY-RUN] Would delete web jump: {item.get('name')} (ID: {item_id})")
|
|
560
|
+
return
|
|
561
|
+
|
|
562
|
+
if not force:
|
|
563
|
+
item = client.get_web_jump(item_id)
|
|
564
|
+
typer.confirm(f"Delete web jump '{item.get('name')}' (ID: {item_id})?", abort=True)
|
|
565
|
+
|
|
512
566
|
client.delete_web_jump(item_id)
|
|
513
567
|
print_success(f"Deleted web jump {item_id}")
|
|
514
568
|
except httpx.HTTPStatusError as e:
|
|
@@ -530,7 +584,10 @@ app.add_typer(tunnel_app, name="tunnel")
|
|
|
530
584
|
@tunnel_app.command("list")
|
|
531
585
|
def list_protocol_tunnels(
|
|
532
586
|
jump_group_id: Optional[int] = typer.Option(None, "--jump-group", "-g", help="Filter by Jump Group"),
|
|
533
|
-
tunnel_type: Optional[str] = typer.Option(None, "--type",
|
|
587
|
+
tunnel_type: Optional[str] = typer.Option(None, "--type", help="Filter by type: tcp, mssql, psql, mysql, k8s"),
|
|
588
|
+
tag: Optional[str] = typer.Option(None, "--tag", "-t", help="Filter by tag (exact match)"),
|
|
589
|
+
name: Optional[str] = typer.Option(None, "--name", "-n", help="Filter by name (exact match)"),
|
|
590
|
+
hostname: Optional[str] = typer.Option(None, "--hostname", help="Filter by hostname (exact match)"),
|
|
534
591
|
output: OutputFormat = typer.Option(OutputFormat.TABLE, "--output", "-o"),
|
|
535
592
|
):
|
|
536
593
|
"""List Protocol Tunnel Jump items."""
|
|
@@ -538,7 +595,13 @@ def list_protocol_tunnels(
|
|
|
538
595
|
|
|
539
596
|
try:
|
|
540
597
|
client = get_client()
|
|
541
|
-
items = client.list_protocol_tunnels(
|
|
598
|
+
items = client.list_protocol_tunnels(
|
|
599
|
+
jump_group_id=jump_group_id,
|
|
600
|
+
tunnel_type=tunnel_type,
|
|
601
|
+
tag=tag,
|
|
602
|
+
name=name,
|
|
603
|
+
hostname=hostname,
|
|
604
|
+
)
|
|
542
605
|
|
|
543
606
|
if output == OutputFormat.JSON:
|
|
544
607
|
print_json(items)
|
|
@@ -680,15 +743,22 @@ def create_protocol_tunnel(
|
|
|
680
743
|
def delete_protocol_tunnel(
|
|
681
744
|
item_id: int = typer.Argument(..., help="Protocol Tunnel ID"),
|
|
682
745
|
force: bool = typer.Option(False, "--force", "-f", help="Skip confirmation"),
|
|
746
|
+
dry_run: bool = typer.Option(False, "--dry-run", help="Show what would be deleted without deleting"),
|
|
683
747
|
):
|
|
684
748
|
"""Delete a Protocol Tunnel Jump item."""
|
|
685
749
|
from bt_cli.pra.client import get_client
|
|
686
750
|
|
|
687
|
-
if not force:
|
|
688
|
-
typer.confirm(f"Delete protocol tunnel {item_id}?", abort=True)
|
|
689
|
-
|
|
690
751
|
try:
|
|
691
752
|
client = get_client()
|
|
753
|
+
if dry_run:
|
|
754
|
+
item = client.get_protocol_tunnel(item_id)
|
|
755
|
+
print_success(f"[DRY-RUN] Would delete protocol tunnel: {item.get('name')} (ID: {item_id})")
|
|
756
|
+
return
|
|
757
|
+
|
|
758
|
+
if not force:
|
|
759
|
+
item = client.get_protocol_tunnel(item_id)
|
|
760
|
+
typer.confirm(f"Delete protocol tunnel '{item.get('name')}' (ID: {item_id})?", abort=True)
|
|
761
|
+
|
|
692
762
|
client.delete_protocol_tunnel(item_id)
|
|
693
763
|
print_success(f"Deleted protocol tunnel {item_id}")
|
|
694
764
|
except httpx.HTTPStatusError as e:
|
|
@@ -867,3 +867,46 @@ class BeyondInsightMixin:
|
|
|
867
867
|
Attribute object
|
|
868
868
|
"""
|
|
869
869
|
return self.get(f"/Attributes/{attribute_id}")
|
|
870
|
+
|
|
871
|
+
def get_managed_system_attributes(
|
|
872
|
+
self: "PasswordSafeClient",
|
|
873
|
+
managed_system_id: int,
|
|
874
|
+
) -> list[dict[str, Any]]:
|
|
875
|
+
"""Get attributes for a managed system.
|
|
876
|
+
|
|
877
|
+
Args:
|
|
878
|
+
managed_system_id: Managed system ID
|
|
879
|
+
|
|
880
|
+
Returns:
|
|
881
|
+
List of attribute objects assigned to this system
|
|
882
|
+
"""
|
|
883
|
+
return self.get(f"/ManagedSystems/{managed_system_id}/Attributes")
|
|
884
|
+
|
|
885
|
+
def assign_managed_system_attribute(
|
|
886
|
+
self: "PasswordSafeClient",
|
|
887
|
+
managed_system_id: int,
|
|
888
|
+
attribute_id: int,
|
|
889
|
+
) -> dict[str, Any]:
|
|
890
|
+
"""Assign an attribute to a managed system.
|
|
891
|
+
|
|
892
|
+
Args:
|
|
893
|
+
managed_system_id: Managed system ID
|
|
894
|
+
attribute_id: Attribute ID to assign
|
|
895
|
+
|
|
896
|
+
Returns:
|
|
897
|
+
The created assignment
|
|
898
|
+
"""
|
|
899
|
+
return self.post(f"/ManagedSystems/{managed_system_id}/Attributes/{attribute_id}")
|
|
900
|
+
|
|
901
|
+
def remove_managed_system_attribute(
|
|
902
|
+
self: "PasswordSafeClient",
|
|
903
|
+
managed_system_id: int,
|
|
904
|
+
attribute_id: int,
|
|
905
|
+
) -> None:
|
|
906
|
+
"""Remove an attribute from a managed system.
|
|
907
|
+
|
|
908
|
+
Args:
|
|
909
|
+
managed_system_id: Managed system ID
|
|
910
|
+
attribute_id: Attribute ID to remove
|
|
911
|
+
"""
|
|
912
|
+
self.delete(f"/ManagedSystems/{managed_system_id}/Attributes/{attribute_id}")
|
|
@@ -10,7 +10,7 @@ app = typer.Typer(
|
|
|
10
10
|
|
|
11
11
|
# Import and register command groups
|
|
12
12
|
from . import auth, systems, accounts, assets, workgroups, platforms, users
|
|
13
|
-
from . import credentials, config, databases, directories, clouds, secrets, quick, functional, search
|
|
13
|
+
from . import credentials, config, databases, directories, clouds, secrets, quick, functional, search, attributes
|
|
14
14
|
from .import_export import import_app, export_app
|
|
15
15
|
|
|
16
16
|
app.add_typer(auth.app, name="auth", help="Authentication commands")
|
|
@@ -29,5 +29,6 @@ app.add_typer(secrets.app, name="secrets", help="Secrets Safe (folders and secre
|
|
|
29
29
|
app.add_typer(functional.app, name="functional", help="Functional accounts for auto-management")
|
|
30
30
|
app.add_typer(users.app, name="users", help="Manage users, user groups, and roles")
|
|
31
31
|
app.add_typer(quick.app, name="quick", help="Quick commands - common multi-step operations")
|
|
32
|
+
app.add_typer(attributes.app, name="attributes", help="Manage attributes for systems/accounts")
|
|
32
33
|
app.add_typer(import_app, name="import", help="Import resources from CSV")
|
|
33
34
|
app.add_typer(export_app, name="export", help="Export sample CSV templates")
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
"""Attribute commands for Password Safe."""
|
|
2
|
+
|
|
3
|
+
from typing import Optional
|
|
4
|
+
|
|
5
|
+
import httpx
|
|
6
|
+
import typer
|
|
7
|
+
|
|
8
|
+
from bt_cli.core.output import OutputFormat, print_table, print_json, print_error, print_success, print_api_error
|
|
9
|
+
|
|
10
|
+
app = typer.Typer(no_args_is_help=True, help="Manage attributes")
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@app.command("list")
|
|
14
|
+
def list_attributes(
|
|
15
|
+
attribute_type: Optional[str] = typer.Option(
|
|
16
|
+
None, "--type", "-t",
|
|
17
|
+
help="Filter by type: Asset, ManagedSystem, ManagedAccount"
|
|
18
|
+
),
|
|
19
|
+
output: OutputFormat = typer.Option(OutputFormat.TABLE, "--output", "-o"),
|
|
20
|
+
):
|
|
21
|
+
"""List all defined attributes."""
|
|
22
|
+
from bt_cli.pws.client import get_client
|
|
23
|
+
|
|
24
|
+
try:
|
|
25
|
+
with get_client() as client:
|
|
26
|
+
attributes = client.list_attributes(attribute_type=attribute_type)
|
|
27
|
+
|
|
28
|
+
if output == OutputFormat.JSON:
|
|
29
|
+
print_json(attributes)
|
|
30
|
+
else:
|
|
31
|
+
columns = [
|
|
32
|
+
("ID", "AttributeID"),
|
|
33
|
+
("Name", "AttributeName"),
|
|
34
|
+
("Type", "AttributeType"),
|
|
35
|
+
("Description", "Description"),
|
|
36
|
+
]
|
|
37
|
+
print_table(attributes, columns, title="Attributes")
|
|
38
|
+
except httpx.HTTPStatusError as e:
|
|
39
|
+
print_api_error(e, "list attributes")
|
|
40
|
+
raise typer.Exit(1)
|
|
41
|
+
except httpx.RequestError as e:
|
|
42
|
+
print_api_error(e, "list attributes")
|
|
43
|
+
raise typer.Exit(1)
|
|
44
|
+
except Exception as e:
|
|
45
|
+
print_api_error(e, "list attributes")
|
|
46
|
+
raise typer.Exit(1)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
@app.command("get")
|
|
50
|
+
def get_attribute(
|
|
51
|
+
attribute_id: int = typer.Argument(..., help="Attribute ID"),
|
|
52
|
+
output: OutputFormat = typer.Option(OutputFormat.TABLE, "--output", "-o"),
|
|
53
|
+
):
|
|
54
|
+
"""Get attribute details."""
|
|
55
|
+
from bt_cli.pws.client import get_client
|
|
56
|
+
from rich.console import Console
|
|
57
|
+
from rich.panel import Panel
|
|
58
|
+
|
|
59
|
+
console = Console()
|
|
60
|
+
|
|
61
|
+
try:
|
|
62
|
+
with get_client() as client:
|
|
63
|
+
attr = client.get_attribute(attribute_id)
|
|
64
|
+
|
|
65
|
+
if output == OutputFormat.JSON:
|
|
66
|
+
print_json(attr)
|
|
67
|
+
else:
|
|
68
|
+
name = attr.get("AttributeName", "")
|
|
69
|
+
attr_type = attr.get("AttributeType", "")
|
|
70
|
+
description = attr.get("Description", "") or "-"
|
|
71
|
+
|
|
72
|
+
console.print(Panel(
|
|
73
|
+
f"[bold]{name}[/bold]\n\n"
|
|
74
|
+
f"[dim]Type:[/dim] {attr_type}\n"
|
|
75
|
+
f"[dim]Description:[/dim] {description}",
|
|
76
|
+
title="Attribute Details",
|
|
77
|
+
subtitle=f"ID: {attr.get('AttributeID', '')}",
|
|
78
|
+
))
|
|
79
|
+
except httpx.HTTPStatusError as e:
|
|
80
|
+
print_api_error(e, "get attribute")
|
|
81
|
+
raise typer.Exit(1)
|
|
82
|
+
except httpx.RequestError as e:
|
|
83
|
+
print_api_error(e, "get attribute")
|
|
84
|
+
raise typer.Exit(1)
|
|
85
|
+
except Exception as e:
|
|
86
|
+
print_api_error(e, "get attribute")
|
|
87
|
+
raise typer.Exit(1)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
@app.command("show-for-system")
|
|
91
|
+
def show_system_attributes(
|
|
92
|
+
system_id: int = typer.Argument(..., help="Managed System ID"),
|
|
93
|
+
output: OutputFormat = typer.Option(OutputFormat.TABLE, "--output", "-o"),
|
|
94
|
+
):
|
|
95
|
+
"""Show attributes assigned to a managed system."""
|
|
96
|
+
from bt_cli.pws.client import get_client
|
|
97
|
+
|
|
98
|
+
try:
|
|
99
|
+
with get_client() as client:
|
|
100
|
+
attributes = client.get_managed_system_attributes(system_id)
|
|
101
|
+
|
|
102
|
+
if output == OutputFormat.JSON:
|
|
103
|
+
print_json(attributes)
|
|
104
|
+
else:
|
|
105
|
+
if not attributes:
|
|
106
|
+
print_error(f"No attributes assigned to managed system {system_id}")
|
|
107
|
+
return
|
|
108
|
+
|
|
109
|
+
columns = [
|
|
110
|
+
("ID", "AttributeID"),
|
|
111
|
+
("Name", "AttributeName"),
|
|
112
|
+
("Type", "AttributeType"),
|
|
113
|
+
]
|
|
114
|
+
print_table(attributes, columns, title=f"Attributes for Managed System {system_id}")
|
|
115
|
+
except httpx.HTTPStatusError as e:
|
|
116
|
+
print_api_error(e, "show system attributes")
|
|
117
|
+
raise typer.Exit(1)
|
|
118
|
+
except httpx.RequestError as e:
|
|
119
|
+
print_api_error(e, "show system attributes")
|
|
120
|
+
raise typer.Exit(1)
|
|
121
|
+
except Exception as e:
|
|
122
|
+
print_api_error(e, "show system attributes")
|
|
123
|
+
raise typer.Exit(1)
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
@app.command("assign")
|
|
127
|
+
def assign_attribute(
|
|
128
|
+
system_id: int = typer.Argument(..., help="Managed System ID"),
|
|
129
|
+
attribute_id: int = typer.Argument(..., help="Attribute ID to assign"),
|
|
130
|
+
):
|
|
131
|
+
"""Assign an attribute to a managed system."""
|
|
132
|
+
from bt_cli.pws.client import get_client
|
|
133
|
+
|
|
134
|
+
try:
|
|
135
|
+
with get_client() as client:
|
|
136
|
+
client.assign_managed_system_attribute(system_id, attribute_id)
|
|
137
|
+
print_success(f"Assigned attribute {attribute_id} to managed system {system_id}")
|
|
138
|
+
except httpx.HTTPStatusError as e:
|
|
139
|
+
print_api_error(e, "assign attribute")
|
|
140
|
+
raise typer.Exit(1)
|
|
141
|
+
except httpx.RequestError as e:
|
|
142
|
+
print_api_error(e, "assign attribute")
|
|
143
|
+
raise typer.Exit(1)
|
|
144
|
+
except Exception as e:
|
|
145
|
+
print_api_error(e, "assign attribute")
|
|
146
|
+
raise typer.Exit(1)
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
@app.command("remove")
|
|
150
|
+
def remove_attribute(
|
|
151
|
+
system_id: int = typer.Argument(..., help="Managed System ID"),
|
|
152
|
+
attribute_id: int = typer.Argument(..., help="Attribute ID to remove"),
|
|
153
|
+
force: bool = typer.Option(False, "--force", "-f", help="Skip confirmation"),
|
|
154
|
+
):
|
|
155
|
+
"""Remove an attribute from a managed system."""
|
|
156
|
+
from bt_cli.pws.client import get_client
|
|
157
|
+
|
|
158
|
+
if not force:
|
|
159
|
+
typer.confirm(
|
|
160
|
+
f"Remove attribute {attribute_id} from managed system {system_id}?",
|
|
161
|
+
abort=True
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
try:
|
|
165
|
+
with get_client() as client:
|
|
166
|
+
client.remove_managed_system_attribute(system_id, attribute_id)
|
|
167
|
+
print_success(f"Removed attribute {attribute_id} from managed system {system_id}")
|
|
168
|
+
except httpx.HTTPStatusError as e:
|
|
169
|
+
print_api_error(e, "remove attribute")
|
|
170
|
+
raise typer.Exit(1)
|
|
171
|
+
except httpx.RequestError as e:
|
|
172
|
+
print_api_error(e, "remove attribute")
|
|
173
|
+
raise typer.Exit(1)
|
|
174
|
+
except Exception as e:
|
|
175
|
+
print_api_error(e, "remove attribute")
|
|
176
|
+
raise typer.Exit(1)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|