pal-found-cli 0.1.2__py3-none-any.whl
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.
- pal_found_cli/__init__.py +1 -0
- pal_found_cli/_version.py +24 -0
- pal_found_cli/admin/__init__.py +2 -0
- pal_found_cli/admin/scripts/__init__.py +2 -0
- pal_found_cli/admin/scripts/pal_found_admin_cli.py +600 -0
- pal_found_cli/aip_agents/__init__.py +1 -0
- pal_found_cli/aip_agents/metadata-allow-list.md +20 -0
- pal_found_cli/aip_agents/scripts/__init__.py +1 -0
- pal_found_cli/aip_agents/scripts/pal_found_aip_agents_cli.py +704 -0
- pal_found_cli/audit/__init__.py +1 -0
- pal_found_cli/audit/metadata-allow-list.md +4 -0
- pal_found_cli/audit/scripts/__init__.py +1 -0
- pal_found_cli/audit/scripts/pal_found_audit_cli.py +387 -0
- pal_found_cli/checkpoints/__init__.py +9 -0
- pal_found_cli/checkpoints/metadata-allow-list.md +7 -0
- pal_found_cli/checkpoints/scripts/__init__.py +1 -0
- pal_found_cli/checkpoints/scripts/pal_found_checkpoints_cli.py +527 -0
- pal_found_cli/common/__init__.py +56 -0
- pal_found_cli/common/access_control_guard.py +591 -0
- pal_found_cli/common/async_client_factory.py +161 -0
- pal_found_cli/common/auth_provider.py +82 -0
- pal_found_cli/common/binary_download_handler.py +288 -0
- pal_found_cli/common/config_loader.py +388 -0
- pal_found_cli/common/error_serializer.py +412 -0
- pal_found_cli/common/log_setup.py +241 -0
- pal_found_cli/common/metadata-allow-list.md +169 -0
- pal_found_cli/common/output_formatter.py +267 -0
- pal_found_cli/common/pagination_helper.py +212 -0
- pal_found_cli/common/retry.py +617 -0
- pal_found_cli/common/sdk_error_utils.py +112 -0
- pal_found_cli/common/session_manager.py +461 -0
- pal_found_cli/common/tracing_provider.py +107 -0
- pal_found_cli/connectivity/__init__.py +9 -0
- pal_found_cli/connectivity/metadata-allow-list.md +24 -0
- pal_found_cli/connectivity/scripts/__init__.py +1 -0
- pal_found_cli/connectivity/scripts/pal_found_connectivity_cli.py +720 -0
- pal_found_cli/data_health/__init__.py +9 -0
- pal_found_cli/data_health/metadata-allow-list.md +10 -0
- pal_found_cli/data_health/scripts/__init__.py +1 -0
- pal_found_cli/data_health/scripts/pal_found_data_health_cli.py +458 -0
- pal_found_cli/datasets/__init__.py +1 -0
- pal_found_cli/datasets/scripts/__init__.py +1 -0
- pal_found_cli/datasets/scripts/pal_found_datasets_cli.py +732 -0
- pal_found_cli/filesystem/__init__.py +2 -0
- pal_found_cli/filesystem/scripts/__init__.py +2 -0
- pal_found_cli/filesystem/scripts/pal_found_filesystem_cli.py +659 -0
- pal_found_cli/functions/__init__.py +2 -0
- pal_found_cli/functions/scripts/__init__.py +2 -0
- pal_found_cli/functions/scripts/pal_found_functions_cli.py +389 -0
- pal_found_cli/language_models/__init__.py +1 -0
- pal_found_cli/language_models/metadata-allow-list.md +6 -0
- pal_found_cli/language_models/scripts/__init__.py +1 -0
- pal_found_cli/language_models/scripts/pal_found_language_models_cli.py +342 -0
- pal_found_cli/media_sets/__init__.py +9 -0
- pal_found_cli/media_sets/metadata-allow-list.md +23 -0
- pal_found_cli/media_sets/scripts/__init__.py +1 -0
- pal_found_cli/media_sets/scripts/pal_found_media_sets_cli.py +642 -0
- pal_found_cli/models/__init__.py +5 -0
- pal_found_cli/models/metadata-allow-list.md +27 -0
- pal_found_cli/models/scripts/__init__.py +1 -0
- pal_found_cli/models/scripts/pal_found_models_cli.py +714 -0
- pal_found_cli/ontologies/__init__.py +1 -0
- pal_found_cli/ontologies/scripts/__init__.py +1 -0
- pal_found_cli/ontologies/scripts/pal_found_ontologies_cli.py +1225 -0
- pal_found_cli/orchestration/__init__.py +9 -0
- pal_found_cli/orchestration/metadata-allow-list.md +24 -0
- pal_found_cli/orchestration/scripts/__init__.py +1 -0
- pal_found_cli/orchestration/scripts/pal_found_orchestration_cli.py +640 -0
- pal_found_cli/sql_queries/__init__.py +9 -0
- pal_found_cli/sql_queries/metadata-allow-list.md +9 -0
- pal_found_cli/sql_queries/scripts/__init__.py +1 -0
- pal_found_cli/sql_queries/scripts/pal_found_sql_queries_cli.py +514 -0
- pal_found_cli/streams/__init__.py +9 -0
- pal_found_cli/streams/metadata-allow-list.md +19 -0
- pal_found_cli/streams/scripts/__init__.py +1 -0
- pal_found_cli/streams/scripts/pal_found_streams_cli.py +615 -0
- pal_found_cli/third_party_applications/__init__.py +9 -0
- pal_found_cli/third_party_applications/metadata-allow-list.md +13 -0
- pal_found_cli/third_party_applications/scripts/__init__.py +1 -0
- pal_found_cli/third_party_applications/scripts/pal_found_third_party_applications_cli.py +547 -0
- pal_found_cli/widgets/__init__.py +9 -0
- pal_found_cli/widgets/metadata-allow-list.md +12 -0
- pal_found_cli/widgets/scripts/__init__.py +1 -0
- pal_found_cli/widgets/scripts/pal_found_widgets_cli.py +571 -0
- pal_found_cli-0.1.2.dist-info/METADATA +74 -0
- pal_found_cli-0.1.2.dist-info/RECORD +89 -0
- pal_found_cli-0.1.2.dist-info/WHEEL +5 -0
- pal_found_cli-0.1.2.dist-info/entry_points.txt +19 -0
- pal_found_cli-0.1.2.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Foundry CLI — Agentic toolset for Palantir Foundry API v2."""
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# file generated by vcs-versioning
|
|
2
|
+
# don't change, don't track in version control
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
__all__ = [
|
|
6
|
+
"__version__",
|
|
7
|
+
"__version_tuple__",
|
|
8
|
+
"version",
|
|
9
|
+
"version_tuple",
|
|
10
|
+
"__commit_id__",
|
|
11
|
+
"commit_id",
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
version: str
|
|
15
|
+
__version__: str
|
|
16
|
+
__version_tuple__: tuple[int | str, ...]
|
|
17
|
+
version_tuple: tuple[int | str, ...]
|
|
18
|
+
commit_id: str | None
|
|
19
|
+
__commit_id__: str | None
|
|
20
|
+
|
|
21
|
+
__version__ = version = '0.1.2'
|
|
22
|
+
__version_tuple__ = version_tuple = (0, 1, 2)
|
|
23
|
+
|
|
24
|
+
__commit_id__ = commit_id = None
|
|
@@ -0,0 +1,600 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Foundry Admin CLI - 66 canonical API v2 operations."""
|
|
3
|
+
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
import argparse
|
|
7
|
+
import asyncio
|
|
8
|
+
import inspect
|
|
9
|
+
import json
|
|
10
|
+
import logging
|
|
11
|
+
import math
|
|
12
|
+
import sys
|
|
13
|
+
from collections.abc import Iterable
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
from typing import Any
|
|
16
|
+
|
|
17
|
+
_SCRIPT_DIR = Path(__file__).resolve().parent
|
|
18
|
+
_PROJECT_ROOT: str | None = None
|
|
19
|
+
_candidate = _SCRIPT_DIR
|
|
20
|
+
for _depth in range(8):
|
|
21
|
+
if (_candidate / "src" / "pal_found_cli" / "__init__.py").exists():
|
|
22
|
+
_PROJECT_ROOT = str(_candidate)
|
|
23
|
+
break
|
|
24
|
+
if _candidate.parent == _candidate:
|
|
25
|
+
break
|
|
26
|
+
_candidate = _candidate.parent
|
|
27
|
+
if _PROJECT_ROOT is None:
|
|
28
|
+
_PROJECT_ROOT = str(_SCRIPT_DIR.parents[4])
|
|
29
|
+
if _PROJECT_ROOT not in sys.path:
|
|
30
|
+
sys.path.insert(0, _PROJECT_ROOT)
|
|
31
|
+
|
|
32
|
+
from pal_found_cli.common.access_control_guard import ( # noqa: E402
|
|
33
|
+
AccessControlError,
|
|
34
|
+
AccessControlGuard,
|
|
35
|
+
)
|
|
36
|
+
from pal_found_cli.common.async_client_factory import AsyncClientFactory # noqa: E402
|
|
37
|
+
from pal_found_cli.common.config_loader import ConfigLoader # noqa: E402
|
|
38
|
+
from pal_found_cli.common.error_serializer import ( # noqa: E402
|
|
39
|
+
EXIT_ACCESS_CONTROL,
|
|
40
|
+
EXIT_NOT_FOUND,
|
|
41
|
+
EXIT_PERMISSION_DENIED,
|
|
42
|
+
EXIT_RATE_LIMIT,
|
|
43
|
+
EXIT_SERVER_ERROR,
|
|
44
|
+
EXIT_SUCCESS,
|
|
45
|
+
EXIT_TIMEOUT,
|
|
46
|
+
EXIT_USER_INPUT,
|
|
47
|
+
ErrorSerializer,
|
|
48
|
+
)
|
|
49
|
+
from pal_found_cli.common.log_setup import LogSetup # noqa: E402
|
|
50
|
+
from pal_found_cli.common.output_formatter import OutputFormatter # noqa: E402
|
|
51
|
+
from pal_found_cli.common.pagination_helper import PaginationHelper # noqa: E402
|
|
52
|
+
from pal_found_cli.common.retry import RetryHandler # noqa: E402
|
|
53
|
+
|
|
54
|
+
logger = logging.getLogger(__name__)
|
|
55
|
+
|
|
56
|
+
OperationSpec = dict[str, Any]
|
|
57
|
+
|
|
58
|
+
JSON_ARGS = frozenset(
|
|
59
|
+
{
|
|
60
|
+
"attributes",
|
|
61
|
+
"administrators",
|
|
62
|
+
"body",
|
|
63
|
+
"initial_members",
|
|
64
|
+
"initial_permissions",
|
|
65
|
+
"initial_role_assignments",
|
|
66
|
+
"marking_ids",
|
|
67
|
+
"organizations",
|
|
68
|
+
"principal_ids",
|
|
69
|
+
"role_assignments",
|
|
70
|
+
"where",
|
|
71
|
+
}
|
|
72
|
+
)
|
|
73
|
+
BOOLEAN_ARGS = frozenset({"include_expirations", "preview", "transitive"})
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def _op(
|
|
77
|
+
resource: str,
|
|
78
|
+
operation: str,
|
|
79
|
+
client_path: str,
|
|
80
|
+
method: str,
|
|
81
|
+
positional: Iterable[str] = (),
|
|
82
|
+
required: Iterable[str] = (),
|
|
83
|
+
optional: Iterable[str] = (),
|
|
84
|
+
) -> OperationSpec:
|
|
85
|
+
"""Build an operation specification entry."""
|
|
86
|
+
return {
|
|
87
|
+
"resource": resource,
|
|
88
|
+
"operation": operation,
|
|
89
|
+
"client_path": client_path,
|
|
90
|
+
"method": method,
|
|
91
|
+
"positional": tuple(positional),
|
|
92
|
+
"required": tuple(required),
|
|
93
|
+
"optional": tuple(optional),
|
|
94
|
+
"json_args": frozenset(
|
|
95
|
+
name
|
|
96
|
+
for name in (*positional, *required, *optional)
|
|
97
|
+
if name in JSON_ARGS
|
|
98
|
+
),
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
OP_SPECS: tuple[OperationSpec, ...] = (
|
|
103
|
+
_op(
|
|
104
|
+
"authentication_provider",
|
|
105
|
+
"get",
|
|
106
|
+
"Enrollment.AuthenticationProvider",
|
|
107
|
+
"get",
|
|
108
|
+
("enrollment_rid", "authentication_provider_rid"),
|
|
109
|
+
optional=("preview",),
|
|
110
|
+
),
|
|
111
|
+
_op(
|
|
112
|
+
"authentication_provider",
|
|
113
|
+
"list",
|
|
114
|
+
"Enrollment.AuthenticationProvider",
|
|
115
|
+
"list",
|
|
116
|
+
("enrollment_rid",),
|
|
117
|
+
optional=("preview",),
|
|
118
|
+
),
|
|
119
|
+
_op(
|
|
120
|
+
"authentication_provider",
|
|
121
|
+
"preregister_group",
|
|
122
|
+
"Enrollment.AuthenticationProvider",
|
|
123
|
+
"preregister_group",
|
|
124
|
+
("enrollment_rid", "authentication_provider_rid"),
|
|
125
|
+
required=("name", "organizations"),
|
|
126
|
+
optional=("preview",),
|
|
127
|
+
),
|
|
128
|
+
_op(
|
|
129
|
+
"authentication_provider",
|
|
130
|
+
"preregister_user",
|
|
131
|
+
"Enrollment.AuthenticationProvider",
|
|
132
|
+
"preregister_user",
|
|
133
|
+
("enrollment_rid", "authentication_provider_rid"),
|
|
134
|
+
required=("organization", "username"),
|
|
135
|
+
optional=("attributes", "email", "family_name", "given_name", "preview"),
|
|
136
|
+
),
|
|
137
|
+
_op(
|
|
138
|
+
"cbac_banner",
|
|
139
|
+
"get",
|
|
140
|
+
"CbacBanner",
|
|
141
|
+
"get",
|
|
142
|
+
optional=("display_type", "marking_ids", "preview"),
|
|
143
|
+
),
|
|
144
|
+
_op(
|
|
145
|
+
"cbac_marking_restrictions",
|
|
146
|
+
"get",
|
|
147
|
+
"CbacMarkingRestrictions",
|
|
148
|
+
"get",
|
|
149
|
+
optional=("marking_ids", "preview"),
|
|
150
|
+
),
|
|
151
|
+
_op("enrollment", "get", "Enrollment", "get", ("enrollment_rid",), optional=("preview",)),
|
|
152
|
+
_op("enrollment", "get_current", "Enrollment", "get_current", optional=("preview",)),
|
|
153
|
+
_op(
|
|
154
|
+
"enrollment_role_assignment",
|
|
155
|
+
"add",
|
|
156
|
+
"Enrollment.EnrollmentRoleAssignment",
|
|
157
|
+
"add",
|
|
158
|
+
("enrollment_rid",),
|
|
159
|
+
required=("role_assignments",),
|
|
160
|
+
optional=("preview",),
|
|
161
|
+
),
|
|
162
|
+
_op(
|
|
163
|
+
"enrollment_role_assignment",
|
|
164
|
+
"list",
|
|
165
|
+
"Enrollment.EnrollmentRoleAssignment",
|
|
166
|
+
"list",
|
|
167
|
+
("enrollment_rid",),
|
|
168
|
+
optional=("preview",),
|
|
169
|
+
),
|
|
170
|
+
_op(
|
|
171
|
+
"enrollment_role_assignment",
|
|
172
|
+
"remove",
|
|
173
|
+
"Enrollment.EnrollmentRoleAssignment",
|
|
174
|
+
"remove",
|
|
175
|
+
("enrollment_rid",),
|
|
176
|
+
required=("role_assignments",),
|
|
177
|
+
optional=("preview",),
|
|
178
|
+
),
|
|
179
|
+
_op("group", "create", "Group", "create", required=("attributes", "name", "organizations"), optional=("description",)),
|
|
180
|
+
_op("group", "delete", "Group", "delete", ("group_id",)),
|
|
181
|
+
_op("group", "get", "Group", "get", ("group_id",)),
|
|
182
|
+
_op("group", "get_batch", "Group", "get_batch", ("body",)),
|
|
183
|
+
_op("group", "list", "Group", "list", optional=("page_size", "page_token")),
|
|
184
|
+
_op("group", "list_current", "Group", "list_current", optional=("preview",)),
|
|
185
|
+
_op("group", "replace", "Group", "replace", ("group_id",), required=("attributes", "name", "organizations"), optional=("description",)),
|
|
186
|
+
_op("group", "search", "Group", "search", required=("where",), optional=("page_size", "page_token")),
|
|
187
|
+
_op("group_member", "add", "Group.GroupMember", "add", ("group_id",), required=("principal_ids",), optional=("expiration",)),
|
|
188
|
+
_op("group_member", "list", "Group.GroupMember", "list", ("group_id",), optional=("include_expirations", "page_size", "page_token", "transitive")),
|
|
189
|
+
_op("group_member", "remove", "Group.GroupMember", "remove", ("group_id",), required=("principal_ids",)),
|
|
190
|
+
_op("group_membership", "list", "User.GroupMembership", "list", ("user_id",), optional=("page_size", "page_token", "transitive")),
|
|
191
|
+
_op("group_membership_expiration_policy", "get", "Group.MembershipExpirationPolicy", "get", ("group_id",), optional=("preview",)),
|
|
192
|
+
_op("group_membership_expiration_policy", "replace", "Group.MembershipExpirationPolicy", "replace", ("group_id",), optional=("maximum_duration", "maximum_value", "preview")),
|
|
193
|
+
_op("group_provider_info", "get", "Group.ProviderInfo", "get", ("group_id",)),
|
|
194
|
+
_op("group_provider_info", "replace", "Group.ProviderInfo", "replace", ("group_id",), required=("provider_id",)),
|
|
195
|
+
_op("host", "list", "Enrollment.Host", "list", ("enrollment_rid",), optional=("page_size", "page_token", "preview")),
|
|
196
|
+
_op("marking", "create", "Marking", "create", required=("category_id", "initial_members", "initial_role_assignments", "name"), optional=("description",)),
|
|
197
|
+
_op("marking", "get", "Marking", "get", ("marking_id",)),
|
|
198
|
+
_op("marking", "get_batch", "Marking", "get_batch", ("body",)),
|
|
199
|
+
_op("marking", "list", "Marking", "list", optional=("page_size", "page_token")),
|
|
200
|
+
_op("marking", "replace", "Marking", "replace", ("marking_id",), required=("name",), optional=("description",)),
|
|
201
|
+
_op("marking_category", "create", "MarkingCategory", "create", required=("description", "initial_permissions", "name"), optional=("preview",)),
|
|
202
|
+
_op("marking_category", "get", "MarkingCategory", "get", ("marking_category_id",)),
|
|
203
|
+
_op("marking_category", "list", "MarkingCategory", "list", optional=("page_size", "page_token")),
|
|
204
|
+
_op("marking_category", "replace", "MarkingCategory", "replace", ("marking_category_id",), required=("description", "name"), optional=("preview",)),
|
|
205
|
+
_op("marking_member", "add", "Marking.MarkingMember", "add", ("marking_id",), required=("principal_ids",)),
|
|
206
|
+
_op("marking_member", "list", "Marking.MarkingMember", "list", ("marking_id",), optional=("page_size", "page_token", "transitive")),
|
|
207
|
+
_op("marking_member", "remove", "Marking.MarkingMember", "remove", ("marking_id",), required=("principal_ids",)),
|
|
208
|
+
_op("marking_role_assignment", "add", "Marking.MarkingRoleAssignment", "add", ("marking_id",), required=("role_assignments",)),
|
|
209
|
+
_op("marking_role_assignment", "list", "Marking.MarkingRoleAssignment", "list", ("marking_id",), optional=("page_size", "page_token")),
|
|
210
|
+
_op("marking_role_assignment", "remove", "Marking.MarkingRoleAssignment", "remove", ("marking_id",), required=("role_assignments",)),
|
|
211
|
+
_op("organization", "create", "Organization", "create", required=("administrators", "enrollment_rid", "name"), optional=("description", "host", "preview")),
|
|
212
|
+
_op("organization", "get", "Organization", "get", ("organization_rid",)),
|
|
213
|
+
_op("organization", "list_available_roles", "Organization", "list_available_roles", ("organization_rid",)),
|
|
214
|
+
_op("organization", "replace", "Organization", "replace", ("organization_rid",), required=("name",), optional=("description", "host")),
|
|
215
|
+
_op("organization_guest_member", "add", "Organization.OrganizationGuestMember", "add", ("organization_rid",), required=("principal_ids",), optional=("preview",)),
|
|
216
|
+
_op("organization_guest_member", "list", "Organization.OrganizationGuestMember", "list", ("organization_rid",), optional=("preview",)),
|
|
217
|
+
_op("organization_guest_member", "remove", "Organization.OrganizationGuestMember", "remove", ("organization_rid",), required=("principal_ids",), optional=("preview",)),
|
|
218
|
+
_op("organization_role_assignment", "add", "Organization.OrganizationRoleAssignment", "add", ("organization_rid",), required=("role_assignments",)),
|
|
219
|
+
_op("organization_role_assignment", "list", "Organization.OrganizationRoleAssignment", "list", ("organization_rid",)),
|
|
220
|
+
_op("organization_role_assignment", "remove", "Organization.OrganizationRoleAssignment", "remove", ("organization_rid",), required=("role_assignments",)),
|
|
221
|
+
_op("role", "get", "Role", "get", ("role_id",), optional=("preview",)),
|
|
222
|
+
_op("role", "get_batch", "Role", "get_batch", ("body",), optional=("preview",)),
|
|
223
|
+
_op("user", "delete", "User", "delete", ("user_id",)),
|
|
224
|
+
_op("user", "get", "User", "get", ("user_id",), optional=("status",)),
|
|
225
|
+
_op("user", "get_batch", "User", "get_batch", ("body",)),
|
|
226
|
+
_op("user", "get_current", "User", "get_current"),
|
|
227
|
+
_op("user", "get_markings", "User", "get_markings", ("user_id",)),
|
|
228
|
+
_op("user", "list", "User", "list", optional=("include", "page_size", "page_token")),
|
|
229
|
+
_op("user", "profile_picture", "User", "profile_picture", ("user_id",)),
|
|
230
|
+
_op("user", "revoke_all_tokens", "User", "revoke_all_tokens", ("user_id",)),
|
|
231
|
+
_op("user", "search", "User", "search", required=("where",), optional=("page_size", "page_token")),
|
|
232
|
+
_op("user_provider_info", "get", "User.ProviderInfo", "get", ("user_id",)),
|
|
233
|
+
_op("user_provider_info", "replace", "User.ProviderInfo", "replace", ("user_id",), required=("provider_id",)),
|
|
234
|
+
)
|
|
235
|
+
|
|
236
|
+
OPERATION_BY_RESOURCE: dict[str, dict[str, OperationSpec]] = {}
|
|
237
|
+
for _spec in OP_SPECS:
|
|
238
|
+
OPERATION_BY_RESOURCE.setdefault(_spec["resource"], {})[_spec["operation"]] = _spec
|
|
239
|
+
|
|
240
|
+
PAGINATED_OPS = frozenset(
|
|
241
|
+
{
|
|
242
|
+
("group", "list"),
|
|
243
|
+
("group", "search"),
|
|
244
|
+
("group_member", "list"),
|
|
245
|
+
("group_membership", "list"),
|
|
246
|
+
("host", "list"),
|
|
247
|
+
("marking", "list"),
|
|
248
|
+
("marking_category", "list"),
|
|
249
|
+
("marking_member", "list"),
|
|
250
|
+
("marking_role_assignment", "list"),
|
|
251
|
+
("user", "list"),
|
|
252
|
+
("user", "search"),
|
|
253
|
+
}
|
|
254
|
+
)
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
def _model_to_dict(obj: Any) -> Any:
|
|
258
|
+
"""Convert SDK/Pydantic objects into JSON-serializable values."""
|
|
259
|
+
if obj is None:
|
|
260
|
+
return None
|
|
261
|
+
if isinstance(obj, (bytes, bytearray)):
|
|
262
|
+
return {"bytes": len(obj)}
|
|
263
|
+
if isinstance(obj, list):
|
|
264
|
+
return [_model_to_dict(item) for item in obj]
|
|
265
|
+
if isinstance(obj, dict):
|
|
266
|
+
return {key: _model_to_dict(value) for key, value in obj.items()}
|
|
267
|
+
try:
|
|
268
|
+
result = obj.to_dict()
|
|
269
|
+
if isinstance(result, dict):
|
|
270
|
+
return result
|
|
271
|
+
except (AttributeError, TypeError):
|
|
272
|
+
pass
|
|
273
|
+
try:
|
|
274
|
+
result = obj.model_dump()
|
|
275
|
+
if isinstance(result, dict):
|
|
276
|
+
return result
|
|
277
|
+
except (AttributeError, TypeError):
|
|
278
|
+
pass
|
|
279
|
+
try:
|
|
280
|
+
result = obj.dict()
|
|
281
|
+
if isinstance(result, dict):
|
|
282
|
+
return result
|
|
283
|
+
except (AttributeError, TypeError):
|
|
284
|
+
pass
|
|
285
|
+
return obj
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
def _resolve(resource: str, op: str) -> str:
|
|
289
|
+
"""Resolve CLI kebab-case operation name to SDK snake_case."""
|
|
290
|
+
return op.replace("-", "_")
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
def _kebab(name: str) -> str:
|
|
294
|
+
"""Convert snake_case names to CLI kebab-case."""
|
|
295
|
+
return name.replace("_", "-")
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
def _common_parser(*, paginated: bool) -> argparse.ArgumentParser:
|
|
299
|
+
"""Build shared parser for operation-level common options."""
|
|
300
|
+
common = argparse.ArgumentParser(add_help=False)
|
|
301
|
+
common.add_argument("--timeout", type=int, default=None)
|
|
302
|
+
common.add_argument("--format", choices=["json", "toon", "auto"], default="auto")
|
|
303
|
+
common.add_argument("--pretty", action="store_true")
|
|
304
|
+
if paginated:
|
|
305
|
+
common.add_argument("--page-size", type=int, default=None, dest="page_size")
|
|
306
|
+
common.add_argument("--page-token", type=str, default=None, dest="page_token")
|
|
307
|
+
common.add_argument("--batch-pages", type=int, default=None, dest="batch_pages")
|
|
308
|
+
return common
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
def _add_kwarg(
|
|
312
|
+
parser: argparse.ArgumentParser, arg_name: str, *, required: bool
|
|
313
|
+
) -> None:
|
|
314
|
+
"""Add a named operation argument to the parser."""
|
|
315
|
+
flag = "--" + _kebab(arg_name)
|
|
316
|
+
if arg_name in BOOLEAN_ARGS:
|
|
317
|
+
parser.add_argument(flag, action="store_true", default=None, dest=arg_name)
|
|
318
|
+
return
|
|
319
|
+
parser.add_argument(flag, required=required, default=None, dest=arg_name)
|
|
320
|
+
|
|
321
|
+
|
|
322
|
+
def build_parser() -> argparse.ArgumentParser:
|
|
323
|
+
"""Build argparse parser with all 66 admin operations."""
|
|
324
|
+
parser = argparse.ArgumentParser(
|
|
325
|
+
prog="pal_found_admin_cli",
|
|
326
|
+
description="Foundry Admin CLI - 66 operations",
|
|
327
|
+
)
|
|
328
|
+
subparsers = parser.add_subparsers(dest="resource", help="Resource type")
|
|
329
|
+
|
|
330
|
+
for resource in sorted(OPERATION_BY_RESOURCE):
|
|
331
|
+
res_parser = subparsers.add_parser(_kebab(resource))
|
|
332
|
+
op_sub = res_parser.add_subparsers(dest="operation")
|
|
333
|
+
for operation, spec in sorted(OPERATION_BY_RESOURCE[resource].items()):
|
|
334
|
+
is_paginated = _is_paginated(resource, operation)
|
|
335
|
+
op_parser = op_sub.add_parser(
|
|
336
|
+
_kebab(operation), parents=[_common_parser(paginated=is_paginated)]
|
|
337
|
+
)
|
|
338
|
+
for arg_name in spec["positional"]:
|
|
339
|
+
op_parser.add_argument(arg_name)
|
|
340
|
+
for arg_name in spec["required"]:
|
|
341
|
+
_add_kwarg(op_parser, arg_name, required=True)
|
|
342
|
+
for arg_name in spec["optional"]:
|
|
343
|
+
if arg_name in {"page_size", "page_token"}:
|
|
344
|
+
continue
|
|
345
|
+
_add_kwarg(op_parser, arg_name, required=False)
|
|
346
|
+
|
|
347
|
+
return parser
|
|
348
|
+
|
|
349
|
+
|
|
350
|
+
def _spec_for(resource: str, operation: str) -> OperationSpec:
|
|
351
|
+
"""Return the operation spec for a resource and operation."""
|
|
352
|
+
try:
|
|
353
|
+
return OPERATION_BY_RESOURCE[resource][operation]
|
|
354
|
+
except KeyError as exc:
|
|
355
|
+
raise ValueError(f"Unknown operation: {resource}.{operation}") from exc
|
|
356
|
+
|
|
357
|
+
|
|
358
|
+
def _get_client(
|
|
359
|
+
cfg: ConfigLoader, resource: str, factory: AsyncClientFactory | None = None
|
|
360
|
+
) -> Any:
|
|
361
|
+
"""Get SDK client for an admin resource."""
|
|
362
|
+
root = (factory or AsyncClientFactory()).create(cfg).admin
|
|
363
|
+
spec = next(iter(OPERATION_BY_RESOURCE[resource].values()))
|
|
364
|
+
client = root
|
|
365
|
+
for attr in spec["client_path"].split("."):
|
|
366
|
+
client = getattr(client, attr)
|
|
367
|
+
return client
|
|
368
|
+
|
|
369
|
+
|
|
370
|
+
def _coerce_arg(value: Any, *, json_arg: bool) -> Any:
|
|
371
|
+
"""Parse JSON CLI values for structured SDK arguments."""
|
|
372
|
+
if value is None:
|
|
373
|
+
return None
|
|
374
|
+
if json_arg and isinstance(value, str):
|
|
375
|
+
return json.loads(value)
|
|
376
|
+
return value
|
|
377
|
+
|
|
378
|
+
|
|
379
|
+
async def _resolve_result(value: Any) -> Any:
|
|
380
|
+
"""Await SDK calls that return awaitables."""
|
|
381
|
+
if asyncio.isfuture(value) or inspect.isawaitable(value):
|
|
382
|
+
return await value
|
|
383
|
+
return value
|
|
384
|
+
|
|
385
|
+
|
|
386
|
+
def _is_async_iterator(value: Any) -> bool:
|
|
387
|
+
"""Return true for SDK async resource iterators."""
|
|
388
|
+
return hasattr(value, "__aiter__") and hasattr(value, "__anext__")
|
|
389
|
+
|
|
390
|
+
|
|
391
|
+
async def _read_next_page_token(iterator: Any) -> str | None:
|
|
392
|
+
"""Read SDK async iterator cursor if available."""
|
|
393
|
+
page_iterator = getattr(iterator, "_page_iterator", None)
|
|
394
|
+
token_source = page_iterator if page_iterator is not None else iterator
|
|
395
|
+
getter = getattr(token_source, "get_next_page_token", None)
|
|
396
|
+
if callable(getter):
|
|
397
|
+
token = await _resolve_result(getter())
|
|
398
|
+
return token if isinstance(token, str) else None
|
|
399
|
+
token = getattr(token_source, "next_page_token", None)
|
|
400
|
+
return token if isinstance(token, str) else None
|
|
401
|
+
|
|
402
|
+
|
|
403
|
+
async def _collect_async_iterator(iterator: Any, helper: PaginationHelper) -> list[Any]:
|
|
404
|
+
"""Collect items from a Foundry SDK AsyncResourceIterator."""
|
|
405
|
+
items: list[Any] = []
|
|
406
|
+
item_limit = helper.page_size * helper.batch_pages
|
|
407
|
+
|
|
408
|
+
async for item in iterator:
|
|
409
|
+
items.append(item)
|
|
410
|
+
if len(items) >= item_limit:
|
|
411
|
+
break
|
|
412
|
+
|
|
413
|
+
helper._total_items += len(items)
|
|
414
|
+
helper._pages_fetched += max(1, math.ceil(len(items) / helper.page_size))
|
|
415
|
+
helper._next_page_token = await _read_next_page_token(iterator)
|
|
416
|
+
return items
|
|
417
|
+
|
|
418
|
+
|
|
419
|
+
async def _paginate_page_envelopes(
|
|
420
|
+
first_response: Any,
|
|
421
|
+
call_func: Any,
|
|
422
|
+
helper: PaginationHelper,
|
|
423
|
+
) -> list[Any]:
|
|
424
|
+
"""Collect dict/list page envelopes used by tests and some SDK methods."""
|
|
425
|
+
all_items = helper._extract_items(first_response)
|
|
426
|
+
helper._total_items += len(all_items)
|
|
427
|
+
helper._pages_fetched += 1
|
|
428
|
+
token = helper._extract_next_token(first_response)
|
|
429
|
+
|
|
430
|
+
while token is not None and helper.pages_fetched < helper.batch_pages:
|
|
431
|
+
response = await call_func(page_size=helper.page_size, page_token=token)
|
|
432
|
+
items = helper._extract_items(response)
|
|
433
|
+
all_items.extend(items)
|
|
434
|
+
helper._total_items += len(items)
|
|
435
|
+
helper._pages_fetched += 1
|
|
436
|
+
token = helper._extract_next_token(response)
|
|
437
|
+
|
|
438
|
+
helper._next_page_token = token
|
|
439
|
+
return all_items
|
|
440
|
+
|
|
441
|
+
|
|
442
|
+
async def _invoke(
|
|
443
|
+
resource: str,
|
|
444
|
+
operation: str,
|
|
445
|
+
client: Any,
|
|
446
|
+
args: argparse.Namespace,
|
|
447
|
+
timeout: int | None,
|
|
448
|
+
) -> Any:
|
|
449
|
+
"""Invoke an admin SDK operation."""
|
|
450
|
+
spec = _spec_for(resource, operation)
|
|
451
|
+
method = getattr(client, spec["method"])
|
|
452
|
+
positional = [
|
|
453
|
+
_coerce_arg(getattr(args, name), json_arg=name in spec["json_args"])
|
|
454
|
+
for name in spec["positional"]
|
|
455
|
+
]
|
|
456
|
+
kwargs: dict[str, Any] = {}
|
|
457
|
+
|
|
458
|
+
for name in (*spec["required"], *spec["optional"]):
|
|
459
|
+
value = getattr(args, name, None)
|
|
460
|
+
if value is not None:
|
|
461
|
+
kwargs[name] = _coerce_arg(value, json_arg=name in spec["json_args"])
|
|
462
|
+
|
|
463
|
+
kwargs["request_timeout"] = timeout
|
|
464
|
+
return await _resolve_result(method(*positional, **kwargs))
|
|
465
|
+
|
|
466
|
+
|
|
467
|
+
def _is_paginated(resource: str, operation: str) -> bool:
|
|
468
|
+
"""Return true if operation supports SDK pagination args."""
|
|
469
|
+
return (resource, operation) in PAGINATED_OPS
|
|
470
|
+
|
|
471
|
+
|
|
472
|
+
async def _invoke_paginated(
|
|
473
|
+
resource: str,
|
|
474
|
+
operation: str,
|
|
475
|
+
client: Any,
|
|
476
|
+
args: argparse.Namespace,
|
|
477
|
+
timeout: int | None,
|
|
478
|
+
helper: PaginationHelper,
|
|
479
|
+
) -> Any:
|
|
480
|
+
"""Invoke a paginated admin operation."""
|
|
481
|
+
|
|
482
|
+
async def _single_page(**page_kwargs: Any) -> Any:
|
|
483
|
+
paged_args = argparse.Namespace(**vars(args))
|
|
484
|
+
paged_args.page_size = page_kwargs.get(
|
|
485
|
+
"page_size", getattr(args, "page_size", None)
|
|
486
|
+
)
|
|
487
|
+
paged_args.page_token = page_kwargs.get("page_token", None)
|
|
488
|
+
return await _invoke(resource, operation, client, paged_args, timeout)
|
|
489
|
+
|
|
490
|
+
first_response = await _single_page(**helper.get_sdk_params())
|
|
491
|
+
if _is_async_iterator(first_response):
|
|
492
|
+
return await _collect_async_iterator(first_response, helper)
|
|
493
|
+
return await _paginate_page_envelopes(first_response, _single_page, helper)
|
|
494
|
+
|
|
495
|
+
|
|
496
|
+
async def main() -> int:
|
|
497
|
+
"""Run the Foundry Admin CLI."""
|
|
498
|
+
parser = build_parser()
|
|
499
|
+
args = parser.parse_args()
|
|
500
|
+
if not args.resource:
|
|
501
|
+
parser.print_help()
|
|
502
|
+
return EXIT_USER_INPUT
|
|
503
|
+
if not getattr(args, "operation", None):
|
|
504
|
+
parser.print_help()
|
|
505
|
+
return EXIT_USER_INPUT
|
|
506
|
+
|
|
507
|
+
cfg = ConfigLoader()
|
|
508
|
+
cfg.load()
|
|
509
|
+
LogSetup.configure(log_level=cfg.log_level)
|
|
510
|
+
|
|
511
|
+
resource = args.resource.replace("-", "_")
|
|
512
|
+
operation = _resolve(resource, args.operation)
|
|
513
|
+
logger.info(
|
|
514
|
+
"Executing operation", extra={"resource": resource, "operation": operation}
|
|
515
|
+
)
|
|
516
|
+
|
|
517
|
+
try:
|
|
518
|
+
_spec_for(resource, operation)
|
|
519
|
+
except ValueError as exc:
|
|
520
|
+
ErrorSerializer().serialize(exc)
|
|
521
|
+
return EXIT_USER_INPUT
|
|
522
|
+
|
|
523
|
+
try:
|
|
524
|
+
AccessControlGuard(cfg, "ADMIN").check(resource, operation)
|
|
525
|
+
except AccessControlError as exc:
|
|
526
|
+
ErrorSerializer().serialize(exc)
|
|
527
|
+
return EXIT_ACCESS_CONTROL
|
|
528
|
+
|
|
529
|
+
factory = AsyncClientFactory()
|
|
530
|
+
try:
|
|
531
|
+
with factory.invocation_scope(cfg):
|
|
532
|
+
client = _get_client(cfg, resource, factory)
|
|
533
|
+
timeout = getattr(args, "timeout", None) or getattr(cfg, "timeout_s", None)
|
|
534
|
+
retry_handler = RetryHandler()
|
|
535
|
+
helper: PaginationHelper | None = None
|
|
536
|
+
if _is_paginated(resource, operation):
|
|
537
|
+
helper = PaginationHelper(
|
|
538
|
+
page_size=getattr(args, "page_size", None),
|
|
539
|
+
page_token=getattr(args, "page_token", None),
|
|
540
|
+
batch_pages=getattr(args, "batch_pages", None),
|
|
541
|
+
)
|
|
542
|
+
if helper is not None:
|
|
543
|
+
result = await retry_handler.execute(
|
|
544
|
+
_invoke_paginated,
|
|
545
|
+
resource,
|
|
546
|
+
operation,
|
|
547
|
+
client,
|
|
548
|
+
args,
|
|
549
|
+
timeout,
|
|
550
|
+
helper,
|
|
551
|
+
)
|
|
552
|
+
else:
|
|
553
|
+
result = await retry_handler.execute(
|
|
554
|
+
_invoke,
|
|
555
|
+
resource,
|
|
556
|
+
operation,
|
|
557
|
+
client,
|
|
558
|
+
args,
|
|
559
|
+
timeout,
|
|
560
|
+
)
|
|
561
|
+
formatter = OutputFormatter(
|
|
562
|
+
format_setting=getattr(args, "format", "auto"),
|
|
563
|
+
pretty=getattr(args, "pretty", False),
|
|
564
|
+
)
|
|
565
|
+
print(formatter.format(_model_to_dict(result)))
|
|
566
|
+
if helper is not None:
|
|
567
|
+
helper.emit_metadata()
|
|
568
|
+
return EXIT_SUCCESS
|
|
569
|
+
except PermissionError as exc:
|
|
570
|
+
ErrorSerializer().serialize(exc)
|
|
571
|
+
return EXIT_PERMISSION_DENIED
|
|
572
|
+
except FileNotFoundError as exc:
|
|
573
|
+
ErrorSerializer().serialize(exc)
|
|
574
|
+
return EXIT_NOT_FOUND
|
|
575
|
+
except TimeoutError as exc:
|
|
576
|
+
ErrorSerializer().serialize(exc)
|
|
577
|
+
return EXIT_TIMEOUT
|
|
578
|
+
except ValueError as exc:
|
|
579
|
+
ErrorSerializer().serialize(exc)
|
|
580
|
+
return EXIT_USER_INPUT
|
|
581
|
+
except OSError as exc:
|
|
582
|
+
exit_code = ErrorSerializer().serialize(exc)
|
|
583
|
+
if getattr(exc, "errno", None) in (11, 115):
|
|
584
|
+
return EXIT_RATE_LIMIT
|
|
585
|
+
return exit_code
|
|
586
|
+
except Exception as exc:
|
|
587
|
+
exit_code = ErrorSerializer().serialize(exc)
|
|
588
|
+
if exit_code != EXIT_USER_INPUT:
|
|
589
|
+
return exit_code
|
|
590
|
+
return EXIT_SERVER_ERROR
|
|
591
|
+
|
|
592
|
+
|
|
593
|
+
def console_main() -> int:
|
|
594
|
+
"""Run the async CLI from the console script entry point."""
|
|
595
|
+
return asyncio.run(main())
|
|
596
|
+
|
|
597
|
+
|
|
598
|
+
if __name__ == "__main__":
|
|
599
|
+
sys.exit(console_main())
|
|
600
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Foundry AIP Agents command-line namespace."""
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# AIP Agents metadata allow-list
|
|
2
|
+
|
|
3
|
+
| SDK Path | Status | Rationale |
|
|
4
|
+
|---|---|---|
|
|
5
|
+
| `aip_agents.agent.all_sessions` | BLOCKED | Cross-agent session content |
|
|
6
|
+
| `aip_agents.agent.get` | PERMITTED | Agent metadata |
|
|
7
|
+
| `aip_agents.agent_version.get` | PERMITTED | Version metadata |
|
|
8
|
+
| `aip_agents.agent_version.list` | PERMITTED | Version metadata |
|
|
9
|
+
| `aip_agents.content.get` | BLOCKED | Session content |
|
|
10
|
+
| `aip_agents.session.blocking_continue` | BLOCKED | Mutates session |
|
|
11
|
+
| `aip_agents.session.cancel` | BLOCKED | Mutates session |
|
|
12
|
+
| `aip_agents.session.create` | BLOCKED | Creates session |
|
|
13
|
+
| `aip_agents.session.delete` | BLOCKED | Deletes session |
|
|
14
|
+
| `aip_agents.session.get` | PERMITTED | Session metadata |
|
|
15
|
+
| `aip_agents.session.list` | PERMITTED | Session metadata |
|
|
16
|
+
| `aip_agents.session.purge` | BLOCKED | Deletes local state |
|
|
17
|
+
| `aip_agents.session.rag_context` | BLOCKED | Processes session input |
|
|
18
|
+
| `aip_agents.session.streaming_continue` | BLOCKED | Mutates session and returns content |
|
|
19
|
+
| `aip_agents.session.update_title` | BLOCKED | Mutates session |
|
|
20
|
+
| `aip_agents.session_trace.get` | PERMITTED | Trace metadata |
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Executable modules for Foundry AIP Agents."""
|