bt-cli 0.4.25__tar.gz → 0.4.27__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.27}/CLAUDE.md +1 -1
- {bt_cli-0.4.25 → bt_cli-0.4.27}/PKG-INFO +1 -1
- {bt_cli-0.4.25 → bt_cli-0.4.27}/pyproject.toml +1 -1
- {bt_cli-0.4.25 → bt_cli-0.4.27}/src/bt_cli/__init__.py +1 -1
- {bt_cli-0.4.25 → bt_cli-0.4.27}/src/bt_cli/pra/client/base.py +49 -5
- {bt_cli-0.4.25 → bt_cli-0.4.27}/src/bt_cli/pra/commands/jump_items.py +203 -27
- {bt_cli-0.4.25 → bt_cli-0.4.27}/src/bt_cli/pws/client/beyondinsight.py +86 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/src/bt_cli/pws/commands/__init__.py +2 -1
- bt_cli-0.4.27/src/bt_cli/pws/commands/attributes.py +266 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/.claude/skills/bt/SKILL.md +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/.claude/skills/entitle/SKILL.md +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/.claude/skills/epmw/SKILL.md +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/.claude/skills/pra/SKILL.md +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/.claude/skills/pws/SKILL.md +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/.env.example +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/.github/workflows/ci.yml +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/.github/workflows/release.yml +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/.gitignore +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/README.md +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/assets/cli-help.png +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/assets/cli-output.png +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/bt-cli.spec +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/bt_entry.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/scripts/bt_entry.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/scripts/sync-package-data.sh +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/src/bt_cli/cli.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/src/bt_cli/commands/__init__.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/src/bt_cli/commands/configure.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/src/bt_cli/commands/learn.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/src/bt_cli/commands/quick.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/src/bt_cli/core/__init__.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/src/bt_cli/core/auth.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/src/bt_cli/core/client.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/src/bt_cli/core/config.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/src/bt_cli/core/config_file.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/src/bt_cli/core/csv_utils.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/src/bt_cli/core/errors.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/src/bt_cli/core/output.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/src/bt_cli/core/prompts.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/src/bt_cli/core/rest_debug.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/src/bt_cli/data/CLAUDE.md +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/src/bt_cli/data/__init__.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/src/bt_cli/data/skills/bt/SKILL.md +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/src/bt_cli/data/skills/entitle/SKILL.md +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/src/bt_cli/data/skills/epmw/SKILL.md +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/src/bt_cli/data/skills/pra/SKILL.md +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/src/bt_cli/data/skills/pws/SKILL.md +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/src/bt_cli/entitle/__init__.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/src/bt_cli/entitle/client/__init__.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/src/bt_cli/entitle/client/base.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/src/bt_cli/entitle/commands/__init__.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/src/bt_cli/entitle/commands/accounts.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/src/bt_cli/entitle/commands/applications.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/src/bt_cli/entitle/commands/auth.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/src/bt_cli/entitle/commands/bundles.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/src/bt_cli/entitle/commands/integrations.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/src/bt_cli/entitle/commands/permissions.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/src/bt_cli/entitle/commands/policies.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/src/bt_cli/entitle/commands/resources.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/src/bt_cli/entitle/commands/roles.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/src/bt_cli/entitle/commands/users.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/src/bt_cli/entitle/commands/workflows.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/src/bt_cli/entitle/models/__init__.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/src/bt_cli/entitle/models/bundle.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/src/bt_cli/entitle/models/common.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/src/bt_cli/entitle/models/integration.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/src/bt_cli/entitle/models/permission.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/src/bt_cli/entitle/models/policy.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/src/bt_cli/entitle/models/resource.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/src/bt_cli/entitle/models/role.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/src/bt_cli/entitle/models/user.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/src/bt_cli/entitle/models/workflow.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/src/bt_cli/epmw/__init__.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/src/bt_cli/epmw/client/__init__.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/src/bt_cli/epmw/client/base.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/src/bt_cli/epmw/commands/__init__.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/src/bt_cli/epmw/commands/audits.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/src/bt_cli/epmw/commands/auth.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/src/bt_cli/epmw/commands/computers.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/src/bt_cli/epmw/commands/events.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/src/bt_cli/epmw/commands/groups.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/src/bt_cli/epmw/commands/policies.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/src/bt_cli/epmw/commands/quick.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/src/bt_cli/epmw/commands/requests.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/src/bt_cli/epmw/commands/roles.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/src/bt_cli/epmw/commands/tasks.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/src/bt_cli/epmw/commands/users.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/src/bt_cli/epmw/models/__init__.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/src/bt_cli/pra/__init__.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/src/bt_cli/pra/client/__init__.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/src/bt_cli/pra/commands/__init__.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/src/bt_cli/pra/commands/auth.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/src/bt_cli/pra/commands/import_export.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/src/bt_cli/pra/commands/jump_clients.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/src/bt_cli/pra/commands/jump_groups.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/src/bt_cli/pra/commands/jumpoints.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/src/bt_cli/pra/commands/policies.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/src/bt_cli/pra/commands/quick.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/src/bt_cli/pra/commands/teams.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/src/bt_cli/pra/commands/users.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/src/bt_cli/pra/commands/vault.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/src/bt_cli/pra/models/__init__.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/src/bt_cli/pra/models/common.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/src/bt_cli/pra/models/jump_client.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/src/bt_cli/pra/models/jump_group.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/src/bt_cli/pra/models/jump_item.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/src/bt_cli/pra/models/jumpoint.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/src/bt_cli/pra/models/team.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/src/bt_cli/pra/models/user.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/src/bt_cli/pra/models/vault.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/src/bt_cli/pws/__init__.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/src/bt_cli/pws/client/__init__.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/src/bt_cli/pws/client/base.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/src/bt_cli/pws/client/passwordsafe.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/src/bt_cli/pws/commands/accounts.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/src/bt_cli/pws/commands/assets.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/src/bt_cli/pws/commands/auth.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/src/bt_cli/pws/commands/clouds.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/src/bt_cli/pws/commands/config.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/src/bt_cli/pws/commands/credentials.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/src/bt_cli/pws/commands/databases.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/src/bt_cli/pws/commands/directories.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/src/bt_cli/pws/commands/functional.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/src/bt_cli/pws/commands/import_export.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/src/bt_cli/pws/commands/platforms.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/src/bt_cli/pws/commands/quick.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/src/bt_cli/pws/commands/search.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/src/bt_cli/pws/commands/secrets.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/src/bt_cli/pws/commands/systems.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/src/bt_cli/pws/commands/users.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/src/bt_cli/pws/commands/workgroups.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/src/bt_cli/pws/config.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/src/bt_cli/pws/models/__init__.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/src/bt_cli/pws/models/account.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/src/bt_cli/pws/models/asset.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/src/bt_cli/pws/models/common.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/src/bt_cli/pws/models/system.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/tests/__init__.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/tests/conftest.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/tests/core/__init__.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/tests/core/test_auth.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/tests/core/test_config.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/tests/core/test_errors.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/tests/core/test_rest_debug.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/tests/entitle/__init__.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/tests/entitle/test_client.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/tests/entitle/test_commands.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/tests/entitle-smoke-test.sh +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/tests/epmw/__init__.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/tests/epmw/test_client.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/tests/epmw/test_commands.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/tests/epmw-quick-test-plan.md +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/tests/fixtures/__init__.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/tests/fixtures/responses.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/tests/integration/__init__.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/tests/integration/conftest.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/tests/integration/helpers.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/tests/integration/test_entitle_integration.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/tests/integration/test_epmw_integration.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/tests/integration/test_epmw_lifecycle.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/tests/integration/test_pra_integration.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/tests/integration/test_pra_lifecycle.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/tests/integration/test_pws_integration.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/tests/integration/test_pws_lifecycle.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/tests/pra/__init__.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/tests/pra/test_client.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/tests/pra/test_commands.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/tests/pra-smoke-test.sh +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/tests/pra-test-plan.md +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/tests/pws/__init__.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/tests/pws/test_client.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/tests/pws/test_commands.py +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/tests/pws-quick-test-plan.md +0 -0
- {bt_cli-0.4.25 → bt_cli-0.4.27}/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]:
|
|
@@ -1,12 +1,29 @@
|
|
|
1
1
|
"""Jump Item commands (shell, RDP, VNC, web, tunnels)."""
|
|
2
2
|
|
|
3
|
-
from
|
|
3
|
+
from fnmatch import fnmatch
|
|
4
|
+
from typing import List, Optional
|
|
4
5
|
|
|
5
6
|
import httpx
|
|
6
7
|
import typer
|
|
7
8
|
|
|
8
9
|
from bt_cli.core.output import OutputFormat, print_table, print_json, print_error, print_success, print_api_error
|
|
9
10
|
|
|
11
|
+
|
|
12
|
+
def _filter_by_pattern(items: List[dict], pattern: str, field: str = "name") -> List[dict]:
|
|
13
|
+
"""Filter items by fnmatch pattern on a field."""
|
|
14
|
+
return [item for item in items if fnmatch(item.get(field, "").lower(), pattern.lower())]
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def _find_by_name(items: List[dict], name: str) -> Optional[dict]:
|
|
18
|
+
"""Find a single item by exact name match (case-insensitive)."""
|
|
19
|
+
name_lower = name.lower()
|
|
20
|
+
matches = [item for item in items if item.get("name", "").lower() == name_lower]
|
|
21
|
+
if len(matches) == 1:
|
|
22
|
+
return matches[0]
|
|
23
|
+
elif len(matches) > 1:
|
|
24
|
+
raise ValueError(f"Multiple items found with name '{name}'. Use ID instead.")
|
|
25
|
+
return None
|
|
26
|
+
|
|
10
27
|
app = typer.Typer(no_args_is_help=True)
|
|
11
28
|
|
|
12
29
|
# Shell Jump subcommands
|
|
@@ -18,6 +35,10 @@ app.add_typer(shell_app, name="shell")
|
|
|
18
35
|
def list_shell_jumps(
|
|
19
36
|
jump_group_id: Optional[int] = typer.Option(None, "--jump-group", "-g", help="Filter by Jump Group"),
|
|
20
37
|
jumpoint_id: Optional[int] = typer.Option(None, "--jumpoint", "-j", help="Filter by Jumpoint"),
|
|
38
|
+
tag: Optional[str] = typer.Option(None, "--tag", "-t", help="Filter by tag (exact match)"),
|
|
39
|
+
name: Optional[str] = typer.Option(None, "--name", "-n", help="Filter by name (exact match)"),
|
|
40
|
+
name_pattern: Optional[str] = typer.Option(None, "--name-pattern", help="Filter by name pattern (e.g., 'prod-*')"),
|
|
41
|
+
hostname: Optional[str] = typer.Option(None, "--hostname", help="Filter by hostname (exact match)"),
|
|
21
42
|
output: OutputFormat = typer.Option(OutputFormat.TABLE, "--output", "-o"),
|
|
22
43
|
):
|
|
23
44
|
"""List Shell Jump items."""
|
|
@@ -25,7 +46,17 @@ def list_shell_jumps(
|
|
|
25
46
|
|
|
26
47
|
try:
|
|
27
48
|
client = get_client()
|
|
28
|
-
items = client.list_shell_jumps(
|
|
49
|
+
items = client.list_shell_jumps(
|
|
50
|
+
jump_group_id=jump_group_id,
|
|
51
|
+
jumpoint_id=jumpoint_id,
|
|
52
|
+
tag=tag,
|
|
53
|
+
name=name,
|
|
54
|
+
hostname=hostname,
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
# Client-side pattern filtering
|
|
58
|
+
if name_pattern:
|
|
59
|
+
items = _filter_by_pattern(items, name_pattern)
|
|
29
60
|
|
|
30
61
|
if output == OutputFormat.JSON:
|
|
31
62
|
print_json(items)
|
|
@@ -197,19 +228,44 @@ def update_shell_jump(
|
|
|
197
228
|
|
|
198
229
|
@shell_app.command("delete")
|
|
199
230
|
def delete_shell_jump(
|
|
200
|
-
item_id: int = typer.Argument(
|
|
231
|
+
item_id: Optional[int] = typer.Argument(None, help="Shell Jump ID"),
|
|
232
|
+
name: Optional[str] = typer.Option(None, "--name", "-n", help="Delete by name instead of ID"),
|
|
201
233
|
force: bool = typer.Option(False, "--force", "-f", help="Skip confirmation"),
|
|
234
|
+
dry_run: bool = typer.Option(False, "--dry-run", help="Show what would be deleted without deleting"),
|
|
202
235
|
):
|
|
203
|
-
"""Delete a Shell Jump item."""
|
|
236
|
+
"""Delete a Shell Jump item by ID or name."""
|
|
204
237
|
from bt_cli.pra.client import get_client
|
|
205
238
|
|
|
206
|
-
if not
|
|
207
|
-
|
|
239
|
+
if not item_id and not name:
|
|
240
|
+
print_error("Either ITEM_ID or --name is required")
|
|
241
|
+
raise typer.Exit(1)
|
|
208
242
|
|
|
209
243
|
try:
|
|
210
244
|
client = get_client()
|
|
245
|
+
|
|
246
|
+
# Look up by name if provided
|
|
247
|
+
if name and not item_id:
|
|
248
|
+
items = client.list_shell_jumps(name=name)
|
|
249
|
+
if not items:
|
|
250
|
+
print_error(f"No shell jump found with name '{name}'")
|
|
251
|
+
raise typer.Exit(1)
|
|
252
|
+
if len(items) > 1:
|
|
253
|
+
print_error(f"Multiple shell jumps found with name '{name}'. Use ID instead.")
|
|
254
|
+
raise typer.Exit(1)
|
|
255
|
+
item = items[0]
|
|
256
|
+
item_id = item.get("id")
|
|
257
|
+
else:
|
|
258
|
+
item = client.get_shell_jump(item_id)
|
|
259
|
+
|
|
260
|
+
if dry_run:
|
|
261
|
+
print_success(f"[DRY-RUN] Would delete shell jump: {item.get('name')} (ID: {item_id})")
|
|
262
|
+
return
|
|
263
|
+
|
|
264
|
+
if not force:
|
|
265
|
+
typer.confirm(f"Delete shell jump '{item.get('name')}' (ID: {item_id})?", abort=True)
|
|
266
|
+
|
|
211
267
|
client.delete_shell_jump(item_id)
|
|
212
|
-
print_success(f"Deleted shell jump {item_id}")
|
|
268
|
+
print_success(f"Deleted shell jump '{item.get('name')}' (ID: {item_id})")
|
|
213
269
|
except httpx.HTTPStatusError as e:
|
|
214
270
|
print_api_error(e, "delete shell jump")
|
|
215
271
|
raise typer.Exit(1)
|
|
@@ -230,6 +286,10 @@ app.add_typer(rdp_app, name="rdp")
|
|
|
230
286
|
def list_rdp_jumps(
|
|
231
287
|
jump_group_id: Optional[int] = typer.Option(None, "--jump-group", "-g", help="Filter by Jump Group"),
|
|
232
288
|
jumpoint_id: Optional[int] = typer.Option(None, "--jumpoint", "-j", help="Filter by Jumpoint"),
|
|
289
|
+
tag: Optional[str] = typer.Option(None, "--tag", "-t", help="Filter by tag (exact match)"),
|
|
290
|
+
name: Optional[str] = typer.Option(None, "--name", "-n", help="Filter by name (exact match)"),
|
|
291
|
+
name_pattern: Optional[str] = typer.Option(None, "--name-pattern", help="Filter by name pattern (e.g., 'prod-*')"),
|
|
292
|
+
hostname: Optional[str] = typer.Option(None, "--hostname", help="Filter by hostname (exact match)"),
|
|
233
293
|
output: OutputFormat = typer.Option(OutputFormat.TABLE, "--output", "-o"),
|
|
234
294
|
):
|
|
235
295
|
"""List Remote RDP Jump items."""
|
|
@@ -237,7 +297,16 @@ def list_rdp_jumps(
|
|
|
237
297
|
|
|
238
298
|
try:
|
|
239
299
|
client = get_client()
|
|
240
|
-
items = client.list_rdp_jumps(
|
|
300
|
+
items = client.list_rdp_jumps(
|
|
301
|
+
jump_group_id=jump_group_id,
|
|
302
|
+
jumpoint_id=jumpoint_id,
|
|
303
|
+
tag=tag,
|
|
304
|
+
name=name,
|
|
305
|
+
hostname=hostname,
|
|
306
|
+
)
|
|
307
|
+
|
|
308
|
+
if name_pattern:
|
|
309
|
+
items = _filter_by_pattern(items, name_pattern)
|
|
241
310
|
|
|
242
311
|
if output == OutputFormat.JSON:
|
|
243
312
|
print_json(items)
|
|
@@ -356,19 +425,43 @@ def create_rdp_jump(
|
|
|
356
425
|
|
|
357
426
|
@rdp_app.command("delete")
|
|
358
427
|
def delete_rdp_jump(
|
|
359
|
-
item_id: int = typer.Argument(
|
|
428
|
+
item_id: Optional[int] = typer.Argument(None, help="RDP Jump ID"),
|
|
429
|
+
name: Optional[str] = typer.Option(None, "--name", "-n", help="Delete by name instead of ID"),
|
|
360
430
|
force: bool = typer.Option(False, "--force", "-f", help="Skip confirmation"),
|
|
431
|
+
dry_run: bool = typer.Option(False, "--dry-run", help="Show what would be deleted without deleting"),
|
|
361
432
|
):
|
|
362
|
-
"""Delete a Remote RDP Jump item."""
|
|
433
|
+
"""Delete a Remote RDP Jump item by ID or name."""
|
|
363
434
|
from bt_cli.pra.client import get_client
|
|
364
435
|
|
|
365
|
-
if not
|
|
366
|
-
|
|
436
|
+
if not item_id and not name:
|
|
437
|
+
print_error("Either ITEM_ID or --name is required")
|
|
438
|
+
raise typer.Exit(1)
|
|
367
439
|
|
|
368
440
|
try:
|
|
369
441
|
client = get_client()
|
|
442
|
+
|
|
443
|
+
if name and not item_id:
|
|
444
|
+
items = client.list_rdp_jumps(name=name)
|
|
445
|
+
if not items:
|
|
446
|
+
print_error(f"No RDP jump found with name '{name}'")
|
|
447
|
+
raise typer.Exit(1)
|
|
448
|
+
if len(items) > 1:
|
|
449
|
+
print_error(f"Multiple RDP jumps found with name '{name}'. Use ID instead.")
|
|
450
|
+
raise typer.Exit(1)
|
|
451
|
+
item = items[0]
|
|
452
|
+
item_id = item.get("id")
|
|
453
|
+
else:
|
|
454
|
+
item = client.get_rdp_jump(item_id)
|
|
455
|
+
|
|
456
|
+
if dry_run:
|
|
457
|
+
print_success(f"[DRY-RUN] Would delete RDP jump: {item.get('name')} (ID: {item_id})")
|
|
458
|
+
return
|
|
459
|
+
|
|
460
|
+
if not force:
|
|
461
|
+
typer.confirm(f"Delete RDP jump '{item.get('name')}' (ID: {item_id})?", abort=True)
|
|
462
|
+
|
|
370
463
|
client.delete_rdp_jump(item_id)
|
|
371
|
-
print_success(f"Deleted RDP jump {item_id}")
|
|
464
|
+
print_success(f"Deleted RDP jump '{item.get('name')}' (ID: {item_id})")
|
|
372
465
|
except httpx.HTTPStatusError as e:
|
|
373
466
|
print_api_error(e, "delete RDP jump")
|
|
374
467
|
raise typer.Exit(1)
|
|
@@ -388,6 +481,10 @@ app.add_typer(vnc_app, name="vnc")
|
|
|
388
481
|
@vnc_app.command("list")
|
|
389
482
|
def list_vnc_jumps(
|
|
390
483
|
jump_group_id: Optional[int] = typer.Option(None, "--jump-group", "-g", help="Filter by Jump Group"),
|
|
484
|
+
tag: Optional[str] = typer.Option(None, "--tag", "-t", help="Filter by tag (exact match)"),
|
|
485
|
+
name: Optional[str] = typer.Option(None, "--name", "-n", help="Filter by name (exact match)"),
|
|
486
|
+
name_pattern: Optional[str] = typer.Option(None, "--name-pattern", help="Filter by name pattern (e.g., 'prod-*')"),
|
|
487
|
+
hostname: Optional[str] = typer.Option(None, "--hostname", help="Filter by hostname (exact match)"),
|
|
391
488
|
output: OutputFormat = typer.Option(OutputFormat.TABLE, "--output", "-o"),
|
|
392
489
|
):
|
|
393
490
|
"""List Remote VNC Jump items."""
|
|
@@ -395,7 +492,15 @@ def list_vnc_jumps(
|
|
|
395
492
|
|
|
396
493
|
try:
|
|
397
494
|
client = get_client()
|
|
398
|
-
items = client.list_vnc_jumps(
|
|
495
|
+
items = client.list_vnc_jumps(
|
|
496
|
+
jump_group_id=jump_group_id,
|
|
497
|
+
tag=tag,
|
|
498
|
+
name=name,
|
|
499
|
+
hostname=hostname,
|
|
500
|
+
)
|
|
501
|
+
|
|
502
|
+
if name_pattern:
|
|
503
|
+
items = _filter_by_pattern(items, name_pattern)
|
|
399
504
|
|
|
400
505
|
if output == OutputFormat.JSON:
|
|
401
506
|
print_json(items)
|
|
@@ -427,6 +532,9 @@ app.add_typer(web_app, name="web")
|
|
|
427
532
|
@web_app.command("list")
|
|
428
533
|
def list_web_jumps(
|
|
429
534
|
jump_group_id: Optional[int] = typer.Option(None, "--jump-group", "-g", help="Filter by Jump Group"),
|
|
535
|
+
tag: Optional[str] = typer.Option(None, "--tag", "-t", help="Filter by tag (exact match)"),
|
|
536
|
+
name: Optional[str] = typer.Option(None, "--name", "-n", help="Filter by name (exact match)"),
|
|
537
|
+
name_pattern: Optional[str] = typer.Option(None, "--name-pattern", help="Filter by name pattern (e.g., 'prod-*')"),
|
|
430
538
|
output: OutputFormat = typer.Option(OutputFormat.TABLE, "--output", "-o"),
|
|
431
539
|
):
|
|
432
540
|
"""List Web Jump items."""
|
|
@@ -434,7 +542,14 @@ def list_web_jumps(
|
|
|
434
542
|
|
|
435
543
|
try:
|
|
436
544
|
client = get_client()
|
|
437
|
-
items = client.list_web_jumps(
|
|
545
|
+
items = client.list_web_jumps(
|
|
546
|
+
jump_group_id=jump_group_id,
|
|
547
|
+
tag=tag,
|
|
548
|
+
name=name,
|
|
549
|
+
)
|
|
550
|
+
|
|
551
|
+
if name_pattern:
|
|
552
|
+
items = _filter_by_pattern(items, name_pattern)
|
|
438
553
|
|
|
439
554
|
if output == OutputFormat.JSON:
|
|
440
555
|
print_json(items)
|
|
@@ -498,19 +613,43 @@ def get_web_jump(
|
|
|
498
613
|
|
|
499
614
|
@web_app.command("delete")
|
|
500
615
|
def delete_web_jump(
|
|
501
|
-
item_id: int = typer.Argument(
|
|
616
|
+
item_id: Optional[int] = typer.Argument(None, help="Web Jump ID"),
|
|
617
|
+
name: Optional[str] = typer.Option(None, "--name", "-n", help="Delete by name instead of ID"),
|
|
502
618
|
force: bool = typer.Option(False, "--force", "-f", help="Skip confirmation"),
|
|
619
|
+
dry_run: bool = typer.Option(False, "--dry-run", help="Show what would be deleted without deleting"),
|
|
503
620
|
):
|
|
504
|
-
"""Delete a Web Jump item."""
|
|
621
|
+
"""Delete a Web Jump item by ID or name."""
|
|
505
622
|
from bt_cli.pra.client import get_client
|
|
506
623
|
|
|
507
|
-
if not
|
|
508
|
-
|
|
624
|
+
if not item_id and not name:
|
|
625
|
+
print_error("Either ITEM_ID or --name is required")
|
|
626
|
+
raise typer.Exit(1)
|
|
509
627
|
|
|
510
628
|
try:
|
|
511
629
|
client = get_client()
|
|
630
|
+
|
|
631
|
+
if name and not item_id:
|
|
632
|
+
items = client.list_web_jumps(name=name)
|
|
633
|
+
if not items:
|
|
634
|
+
print_error(f"No web jump found with name '{name}'")
|
|
635
|
+
raise typer.Exit(1)
|
|
636
|
+
if len(items) > 1:
|
|
637
|
+
print_error(f"Multiple web jumps found with name '{name}'. Use ID instead.")
|
|
638
|
+
raise typer.Exit(1)
|
|
639
|
+
item = items[0]
|
|
640
|
+
item_id = item.get("id")
|
|
641
|
+
else:
|
|
642
|
+
item = client.get_web_jump(item_id)
|
|
643
|
+
|
|
644
|
+
if dry_run:
|
|
645
|
+
print_success(f"[DRY-RUN] Would delete web jump: {item.get('name')} (ID: {item_id})")
|
|
646
|
+
return
|
|
647
|
+
|
|
648
|
+
if not force:
|
|
649
|
+
typer.confirm(f"Delete web jump '{item.get('name')}' (ID: {item_id})?", abort=True)
|
|
650
|
+
|
|
512
651
|
client.delete_web_jump(item_id)
|
|
513
|
-
print_success(f"Deleted web jump {item_id}")
|
|
652
|
+
print_success(f"Deleted web jump '{item.get('name')}' (ID: {item_id})")
|
|
514
653
|
except httpx.HTTPStatusError as e:
|
|
515
654
|
print_api_error(e, "delete web jump")
|
|
516
655
|
raise typer.Exit(1)
|
|
@@ -530,7 +669,11 @@ app.add_typer(tunnel_app, name="tunnel")
|
|
|
530
669
|
@tunnel_app.command("list")
|
|
531
670
|
def list_protocol_tunnels(
|
|
532
671
|
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",
|
|
672
|
+
tunnel_type: Optional[str] = typer.Option(None, "--type", help="Filter by type: tcp, mssql, psql, mysql, k8s"),
|
|
673
|
+
tag: Optional[str] = typer.Option(None, "--tag", "-t", help="Filter by tag (exact match)"),
|
|
674
|
+
name: Optional[str] = typer.Option(None, "--name", "-n", help="Filter by name (exact match)"),
|
|
675
|
+
name_pattern: Optional[str] = typer.Option(None, "--name-pattern", help="Filter by name pattern (e.g., 'prod-*')"),
|
|
676
|
+
hostname: Optional[str] = typer.Option(None, "--hostname", help="Filter by hostname (exact match)"),
|
|
534
677
|
output: OutputFormat = typer.Option(OutputFormat.TABLE, "--output", "-o"),
|
|
535
678
|
):
|
|
536
679
|
"""List Protocol Tunnel Jump items."""
|
|
@@ -538,7 +681,16 @@ def list_protocol_tunnels(
|
|
|
538
681
|
|
|
539
682
|
try:
|
|
540
683
|
client = get_client()
|
|
541
|
-
items = client.list_protocol_tunnels(
|
|
684
|
+
items = client.list_protocol_tunnels(
|
|
685
|
+
jump_group_id=jump_group_id,
|
|
686
|
+
tunnel_type=tunnel_type,
|
|
687
|
+
tag=tag,
|
|
688
|
+
name=name,
|
|
689
|
+
hostname=hostname,
|
|
690
|
+
)
|
|
691
|
+
|
|
692
|
+
if name_pattern:
|
|
693
|
+
items = _filter_by_pattern(items, name_pattern)
|
|
542
694
|
|
|
543
695
|
if output == OutputFormat.JSON:
|
|
544
696
|
print_json(items)
|
|
@@ -678,19 +830,43 @@ def create_protocol_tunnel(
|
|
|
678
830
|
|
|
679
831
|
@tunnel_app.command("delete")
|
|
680
832
|
def delete_protocol_tunnel(
|
|
681
|
-
item_id: int = typer.Argument(
|
|
833
|
+
item_id: Optional[int] = typer.Argument(None, help="Protocol Tunnel ID"),
|
|
834
|
+
name: Optional[str] = typer.Option(None, "--name", "-n", help="Delete by name instead of ID"),
|
|
682
835
|
force: bool = typer.Option(False, "--force", "-f", help="Skip confirmation"),
|
|
836
|
+
dry_run: bool = typer.Option(False, "--dry-run", help="Show what would be deleted without deleting"),
|
|
683
837
|
):
|
|
684
|
-
"""Delete a Protocol Tunnel Jump item."""
|
|
838
|
+
"""Delete a Protocol Tunnel Jump item by ID or name."""
|
|
685
839
|
from bt_cli.pra.client import get_client
|
|
686
840
|
|
|
687
|
-
if not
|
|
688
|
-
|
|
841
|
+
if not item_id and not name:
|
|
842
|
+
print_error("Either ITEM_ID or --name is required")
|
|
843
|
+
raise typer.Exit(1)
|
|
689
844
|
|
|
690
845
|
try:
|
|
691
846
|
client = get_client()
|
|
847
|
+
|
|
848
|
+
if name and not item_id:
|
|
849
|
+
items = client.list_protocol_tunnels(name=name)
|
|
850
|
+
if not items:
|
|
851
|
+
print_error(f"No protocol tunnel found with name '{name}'")
|
|
852
|
+
raise typer.Exit(1)
|
|
853
|
+
if len(items) > 1:
|
|
854
|
+
print_error(f"Multiple protocol tunnels found with name '{name}'. Use ID instead.")
|
|
855
|
+
raise typer.Exit(1)
|
|
856
|
+
item = items[0]
|
|
857
|
+
item_id = item.get("id")
|
|
858
|
+
else:
|
|
859
|
+
item = client.get_protocol_tunnel(item_id)
|
|
860
|
+
|
|
861
|
+
if dry_run:
|
|
862
|
+
print_success(f"[DRY-RUN] Would delete protocol tunnel: {item.get('name')} (ID: {item_id})")
|
|
863
|
+
return
|
|
864
|
+
|
|
865
|
+
if not force:
|
|
866
|
+
typer.confirm(f"Delete protocol tunnel '{item.get('name')}' (ID: {item_id})?", abort=True)
|
|
867
|
+
|
|
692
868
|
client.delete_protocol_tunnel(item_id)
|
|
693
|
-
print_success(f"Deleted protocol tunnel {item_id}")
|
|
869
|
+
print_success(f"Deleted protocol tunnel '{item.get('name')}' (ID: {item_id})")
|
|
694
870
|
except httpx.HTTPStatusError as e:
|
|
695
871
|
print_api_error(e, "delete protocol tunnel")
|
|
696
872
|
raise typer.Exit(1)
|
|
@@ -867,3 +867,89 @@ 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}")
|
|
913
|
+
|
|
914
|
+
def get_managed_account_attributes(
|
|
915
|
+
self: "PasswordSafeClient",
|
|
916
|
+
account_id: int,
|
|
917
|
+
) -> list[dict[str, Any]]:
|
|
918
|
+
"""Get attributes for a managed account.
|
|
919
|
+
|
|
920
|
+
Args:
|
|
921
|
+
account_id: Managed account ID
|
|
922
|
+
|
|
923
|
+
Returns:
|
|
924
|
+
List of attribute objects assigned to this account
|
|
925
|
+
"""
|
|
926
|
+
return self.get(f"/ManagedAccounts/{account_id}/Attributes")
|
|
927
|
+
|
|
928
|
+
def assign_managed_account_attribute(
|
|
929
|
+
self: "PasswordSafeClient",
|
|
930
|
+
account_id: int,
|
|
931
|
+
attribute_id: int,
|
|
932
|
+
) -> dict[str, Any]:
|
|
933
|
+
"""Assign an attribute to a managed account.
|
|
934
|
+
|
|
935
|
+
Args:
|
|
936
|
+
account_id: Managed account ID
|
|
937
|
+
attribute_id: Attribute ID to assign
|
|
938
|
+
|
|
939
|
+
Returns:
|
|
940
|
+
The created assignment
|
|
941
|
+
"""
|
|
942
|
+
return self.post(f"/ManagedAccounts/{account_id}/Attributes/{attribute_id}")
|
|
943
|
+
|
|
944
|
+
def remove_managed_account_attribute(
|
|
945
|
+
self: "PasswordSafeClient",
|
|
946
|
+
account_id: int,
|
|
947
|
+
attribute_id: int,
|
|
948
|
+
) -> None:
|
|
949
|
+
"""Remove an attribute from a managed account.
|
|
950
|
+
|
|
951
|
+
Args:
|
|
952
|
+
account_id: Managed account ID
|
|
953
|
+
attribute_id: Attribute ID to remove
|
|
954
|
+
"""
|
|
955
|
+
self.delete(f"/ManagedAccounts/{account_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")
|