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
|
+
"""Console scripts for the Foundry Audit namespace."""
|
|
@@ -0,0 +1,387 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Foundry Audit CLI for log-file listing and bounded content downloads."""
|
|
3
|
+
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
import argparse
|
|
7
|
+
import asyncio
|
|
8
|
+
import json
|
|
9
|
+
import logging
|
|
10
|
+
import re
|
|
11
|
+
import sys
|
|
12
|
+
from collections.abc import Iterable
|
|
13
|
+
from datetime import date
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
from typing import Any, NoReturn
|
|
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.binary_download_handler import ( # noqa: E402
|
|
38
|
+
BinaryDownloadHandler,
|
|
39
|
+
)
|
|
40
|
+
from pal_found_cli.common.config_loader import ConfigLoader # noqa: E402
|
|
41
|
+
from pal_found_cli.common.error_serializer import ( # noqa: E402
|
|
42
|
+
EXIT_ACCESS_CONTROL,
|
|
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
|
+
_ISO_DATE = re.compile(r"\d{4}-\d{2}-\d{2}\Z")
|
|
58
|
+
_METADATA_ALLOWLIST_PATH = Path(__file__).resolve().parents[1] / "metadata-allow-list.md"
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class _ArgumentParser(argparse.ArgumentParser):
|
|
62
|
+
"""Raise user-input exceptions instead of writing argparse diagnostics."""
|
|
63
|
+
|
|
64
|
+
def error(self, message: str) -> NoReturn:
|
|
65
|
+
raise ValueError(message)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def _op(
|
|
69
|
+
operation: str,
|
|
70
|
+
method: str,
|
|
71
|
+
positional: Iterable[str],
|
|
72
|
+
optional: Iterable[str],
|
|
73
|
+
) -> OperationSpec:
|
|
74
|
+
"""Build one immutable-shape operation catalog entry."""
|
|
75
|
+
return {
|
|
76
|
+
"resource": "log_file",
|
|
77
|
+
"operation": operation,
|
|
78
|
+
"client_path": "Organization.LogFile",
|
|
79
|
+
"method": method,
|
|
80
|
+
"positional": tuple(positional),
|
|
81
|
+
"optional": tuple(optional),
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
OP_SPECS: tuple[OperationSpec, ...] = (
|
|
86
|
+
_op(
|
|
87
|
+
"list",
|
|
88
|
+
"list",
|
|
89
|
+
("organization_rid",),
|
|
90
|
+
(
|
|
91
|
+
"start_date",
|
|
92
|
+
"end_date",
|
|
93
|
+
"page_size",
|
|
94
|
+
"page_token",
|
|
95
|
+
"batch_pages",
|
|
96
|
+
),
|
|
97
|
+
),
|
|
98
|
+
_op(
|
|
99
|
+
"content",
|
|
100
|
+
"content",
|
|
101
|
+
("organization_rid", "log_file_id"),
|
|
102
|
+
("output_filename",),
|
|
103
|
+
),
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
OPERATION_BY_RESOURCE: dict[str, dict[str, OperationSpec]] = {
|
|
107
|
+
"log_file": {spec["operation"]: spec for spec in OP_SPECS}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def _common_parser(*, paginated: bool) -> _ArgumentParser:
|
|
112
|
+
"""Build operation-level common options."""
|
|
113
|
+
parser = _ArgumentParser(add_help=False)
|
|
114
|
+
parser.add_argument("--timeout", type=int, default=None)
|
|
115
|
+
parser.add_argument("--format", choices=("json", "toon", "auto"), default="auto")
|
|
116
|
+
parser.add_argument("--pretty", action="store_true")
|
|
117
|
+
if paginated:
|
|
118
|
+
parser.add_argument("--page-size", type=int, default=None, dest="page_size")
|
|
119
|
+
parser.add_argument("--page-token", default=None, dest="page_token")
|
|
120
|
+
parser.add_argument("--batch-pages", type=int, default=None, dest="batch_pages")
|
|
121
|
+
return parser
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def build_parser() -> argparse.ArgumentParser:
|
|
125
|
+
"""Build the parser for the two Foundry Audit operations."""
|
|
126
|
+
parser = _ArgumentParser(
|
|
127
|
+
prog="pal_found_audit_cli",
|
|
128
|
+
description="Foundry Audit CLI - 2 log-file operations",
|
|
129
|
+
epilog="Operations: log-file list; log-file content",
|
|
130
|
+
)
|
|
131
|
+
resource_parsers = parser.add_subparsers(dest="resource", help="Resource type")
|
|
132
|
+
log_file_parser = resource_parsers.add_parser("log-file")
|
|
133
|
+
operation_parsers = log_file_parser.add_subparsers(dest="operation")
|
|
134
|
+
|
|
135
|
+
list_parser = operation_parsers.add_parser(
|
|
136
|
+
"list", parents=[_common_parser(paginated=True)]
|
|
137
|
+
)
|
|
138
|
+
list_parser.add_argument("organization_rid")
|
|
139
|
+
list_parser.add_argument("--start-date", default=None, dest="start_date")
|
|
140
|
+
list_parser.add_argument("--end-date", default=None, dest="end_date")
|
|
141
|
+
|
|
142
|
+
content_parser = operation_parsers.add_parser(
|
|
143
|
+
"content", parents=[_common_parser(paginated=False)]
|
|
144
|
+
)
|
|
145
|
+
content_parser.add_argument("organization_rid")
|
|
146
|
+
content_parser.add_argument("log_file_id")
|
|
147
|
+
content_parser.add_argument(
|
|
148
|
+
"--output-filename", default=None, dest="output_filename"
|
|
149
|
+
)
|
|
150
|
+
return parser
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def _spec_for(resource: str, operation: str) -> OperationSpec:
|
|
154
|
+
"""Return the catalog entry for an Audit operation."""
|
|
155
|
+
try:
|
|
156
|
+
return OPERATION_BY_RESOURCE[resource][operation]
|
|
157
|
+
except KeyError as exc:
|
|
158
|
+
raise ValueError(f"Unknown operation: {resource}.{operation}") from exc
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def _parse_iso_date(value: str | None, *, field: str) -> date | None:
|
|
162
|
+
"""Parse one strict ``YYYY-MM-DD`` calendar date."""
|
|
163
|
+
if value is None:
|
|
164
|
+
return None
|
|
165
|
+
if _ISO_DATE.fullmatch(value) is None:
|
|
166
|
+
raise ValueError(f"{field} must use YYYY-MM-DD format")
|
|
167
|
+
try:
|
|
168
|
+
return date.fromisoformat(value)
|
|
169
|
+
except ValueError as exc:
|
|
170
|
+
raise ValueError(f"{field} must be a valid calendar date") from exc
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def _validate_list_cursor(start_date: date | None, page_token: str | None) -> None:
|
|
174
|
+
"""Require a start date for an initial list request."""
|
|
175
|
+
if start_date is None and not page_token:
|
|
176
|
+
raise ValueError("start_date is required when page_token is not provided")
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def _validate_timeout(value: int) -> int:
|
|
180
|
+
"""Validate the selected per-attempt timeout from ADR-002."""
|
|
181
|
+
if isinstance(value, bool) or not isinstance(value, int) or not 1 <= value <= 3600:
|
|
182
|
+
raise ValueError("timeout must be between 1 and 3600 seconds")
|
|
183
|
+
return value
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
def _get_client(
|
|
187
|
+
cfg: ConfigLoader,
|
|
188
|
+
resource: str,
|
|
189
|
+
factory: AsyncClientFactory | None = None,
|
|
190
|
+
) -> Any:
|
|
191
|
+
"""Create and resolve the nested SDK client for an Audit resource."""
|
|
192
|
+
client = (factory or AsyncClientFactory()).create(cfg).audit
|
|
193
|
+
spec = next(iter(OPERATION_BY_RESOURCE[resource].values()))
|
|
194
|
+
for attr in spec["client_path"].split("."):
|
|
195
|
+
client = getattr(client, attr)
|
|
196
|
+
return client
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
def _model_to_dict(value: Any) -> Any:
|
|
200
|
+
"""Convert SDK and Pydantic models into JSON-compatible values."""
|
|
201
|
+
if value is None:
|
|
202
|
+
return None
|
|
203
|
+
if isinstance(value, list):
|
|
204
|
+
return [_model_to_dict(item) for item in value]
|
|
205
|
+
if isinstance(value, dict):
|
|
206
|
+
return {key: _model_to_dict(item) for key, item in value.items()}
|
|
207
|
+
for method_name in ("to_dict", "model_dump", "dict"):
|
|
208
|
+
method = getattr(value, method_name, None)
|
|
209
|
+
if callable(method):
|
|
210
|
+
result = method()
|
|
211
|
+
if isinstance(result, dict):
|
|
212
|
+
return {key: _model_to_dict(item) for key, item in result.items()}
|
|
213
|
+
return value
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
async def _fetch_list_page(
|
|
217
|
+
client: Any,
|
|
218
|
+
*,
|
|
219
|
+
organization_rid: str,
|
|
220
|
+
start_date: date | None,
|
|
221
|
+
end_date: date | None,
|
|
222
|
+
page_size: int,
|
|
223
|
+
page_token: str | None,
|
|
224
|
+
request_timeout: int | None,
|
|
225
|
+
) -> dict[str, Any]:
|
|
226
|
+
"""Fetch and adapt one raw SDK Audit page."""
|
|
227
|
+
raw_response = await client.with_raw_response.list(
|
|
228
|
+
organization_rid,
|
|
229
|
+
start_date=start_date,
|
|
230
|
+
end_date=end_date,
|
|
231
|
+
page_size=page_size,
|
|
232
|
+
page_token=page_token,
|
|
233
|
+
request_timeout=request_timeout,
|
|
234
|
+
)
|
|
235
|
+
response = raw_response.decode()
|
|
236
|
+
return {
|
|
237
|
+
"items": list(getattr(response, "data", None) or []),
|
|
238
|
+
"next_page_token": getattr(response, "next_page_token", None),
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
async def _list_log_files(
|
|
243
|
+
client: Any,
|
|
244
|
+
args: argparse.Namespace,
|
|
245
|
+
timeout: int | None,
|
|
246
|
+
) -> tuple[list[Any], PaginationHelper]:
|
|
247
|
+
"""Collect one bounded batch of actual server pages."""
|
|
248
|
+
helper = PaginationHelper(
|
|
249
|
+
page_size=args.page_size,
|
|
250
|
+
page_token=args.page_token,
|
|
251
|
+
batch_pages=args.batch_pages,
|
|
252
|
+
)
|
|
253
|
+
|
|
254
|
+
async def fetch_page(**page_kwargs: Any) -> dict[str, Any]:
|
|
255
|
+
return await _fetch_list_page(
|
|
256
|
+
client,
|
|
257
|
+
organization_rid=args.organization_rid,
|
|
258
|
+
start_date=args.start_date,
|
|
259
|
+
end_date=args.end_date,
|
|
260
|
+
page_size=page_kwargs["page_size"],
|
|
261
|
+
page_token=page_kwargs.get("page_token"),
|
|
262
|
+
request_timeout=timeout,
|
|
263
|
+
)
|
|
264
|
+
|
|
265
|
+
records = await helper.paginate(fetch_page)
|
|
266
|
+
return records, helper
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
async def _download_content(
|
|
270
|
+
client: Any,
|
|
271
|
+
args: argparse.Namespace,
|
|
272
|
+
timeout: int | None,
|
|
273
|
+
cfg: ConfigLoader,
|
|
274
|
+
) -> dict[str, Any]:
|
|
275
|
+
"""Stream one log file through the bounded atomic download handler."""
|
|
276
|
+
response_context = client.with_streaming_response.content(
|
|
277
|
+
args.organization_rid,
|
|
278
|
+
args.log_file_id,
|
|
279
|
+
request_timeout=timeout,
|
|
280
|
+
)
|
|
281
|
+
async with response_context as response:
|
|
282
|
+
result = await BinaryDownloadHandler(config=cfg).save(
|
|
283
|
+
response.aiter_bytes(),
|
|
284
|
+
original_filename=args.output_filename,
|
|
285
|
+
namespace="audit",
|
|
286
|
+
operation="log_file.content",
|
|
287
|
+
content_length=None,
|
|
288
|
+
content_encoding=None,
|
|
289
|
+
mime_type=None,
|
|
290
|
+
)
|
|
291
|
+
return result.to_dict()
|
|
292
|
+
|
|
293
|
+
|
|
294
|
+
def _serialize_error(exception: BaseException) -> int:
|
|
295
|
+
"""Serialize an exception and retain the server-error fallback contract."""
|
|
296
|
+
serializer = ErrorSerializer()
|
|
297
|
+
exit_code = serializer.serialize(exception, print_to_stdout=False)
|
|
298
|
+
if exit_code == EXIT_USER_INPUT and not isinstance(exception, (TypeError, ValueError)):
|
|
299
|
+
exit_code = EXIT_SERVER_ERROR
|
|
300
|
+
envelope = serializer.create_error_envelope(
|
|
301
|
+
exit_code,
|
|
302
|
+
str(exception),
|
|
303
|
+
type(exception).__name__,
|
|
304
|
+
serializer.call_id,
|
|
305
|
+
)
|
|
306
|
+
response = getattr(exception, "response", None)
|
|
307
|
+
http_status = getattr(response, "status_code", None)
|
|
308
|
+
if isinstance(http_status, int):
|
|
309
|
+
envelope["http_status"] = http_status
|
|
310
|
+
sys.stdout.write(json.dumps(envelope, default=str) + "\n")
|
|
311
|
+
sys.stdout.flush()
|
|
312
|
+
return exit_code
|
|
313
|
+
|
|
314
|
+
|
|
315
|
+
async def main() -> int:
|
|
316
|
+
"""Run the Foundry Audit CLI."""
|
|
317
|
+
parser = build_parser()
|
|
318
|
+
try:
|
|
319
|
+
args = parser.parse_args()
|
|
320
|
+
if not args.resource or not getattr(args, "operation", None):
|
|
321
|
+
raise ValueError("a log-file operation is required")
|
|
322
|
+
except ValueError as exc:
|
|
323
|
+
return _serialize_error(exc)
|
|
324
|
+
|
|
325
|
+
try:
|
|
326
|
+
cfg = ConfigLoader()
|
|
327
|
+
cfg.load()
|
|
328
|
+
LogSetup.configure(log_level=cfg.log_level)
|
|
329
|
+
|
|
330
|
+
resource = args.resource.replace("-", "_")
|
|
331
|
+
operation = args.operation.replace("-", "_")
|
|
332
|
+
_spec_for(resource, operation)
|
|
333
|
+
|
|
334
|
+
if operation == "list":
|
|
335
|
+
args.start_date = _parse_iso_date(args.start_date, field="start_date")
|
|
336
|
+
args.end_date = _parse_iso_date(args.end_date, field="end_date")
|
|
337
|
+
_validate_list_cursor(args.start_date, args.page_token)
|
|
338
|
+
|
|
339
|
+
timeout = _validate_timeout(
|
|
340
|
+
args.timeout if args.timeout is not None else cfg.timeout_s
|
|
341
|
+
)
|
|
342
|
+
AccessControlGuard(
|
|
343
|
+
cfg,
|
|
344
|
+
"AUDIT",
|
|
345
|
+
metadata_allowlist_path=str(_METADATA_ALLOWLIST_PATH),
|
|
346
|
+
).check(resource, operation)
|
|
347
|
+
|
|
348
|
+
factory = AsyncClientFactory()
|
|
349
|
+
helper: PaginationHelper | None = None
|
|
350
|
+
with factory.invocation_scope(cfg):
|
|
351
|
+
client = _get_client(cfg, resource, factory)
|
|
352
|
+
retry_handler = RetryHandler(timeout_s=timeout)
|
|
353
|
+
if operation == "list":
|
|
354
|
+
result, helper = await retry_handler.execute(
|
|
355
|
+
_list_log_files, client, args, timeout
|
|
356
|
+
)
|
|
357
|
+
else:
|
|
358
|
+
result = await retry_handler.execute(
|
|
359
|
+
_download_content, client, args, timeout, cfg
|
|
360
|
+
)
|
|
361
|
+
|
|
362
|
+
formatter = OutputFormatter(
|
|
363
|
+
format_setting="json" if operation == "content" else args.format,
|
|
364
|
+
pretty=args.pretty,
|
|
365
|
+
)
|
|
366
|
+
print(formatter.format(_model_to_dict(result)))
|
|
367
|
+
if helper is not None:
|
|
368
|
+
helper.emit_metadata()
|
|
369
|
+
return EXIT_SUCCESS
|
|
370
|
+
except AccessControlError as exc:
|
|
371
|
+
ErrorSerializer().serialize(exc)
|
|
372
|
+
return EXIT_ACCESS_CONTROL
|
|
373
|
+
except asyncio.CancelledError:
|
|
374
|
+
cancellation = TimeoutError("Operation cancelled")
|
|
375
|
+
ErrorSerializer().serialize(cancellation)
|
|
376
|
+
return EXIT_TIMEOUT
|
|
377
|
+
except Exception as exc:
|
|
378
|
+
return _serialize_error(exc)
|
|
379
|
+
|
|
380
|
+
|
|
381
|
+
def console_main() -> int:
|
|
382
|
+
"""Run the async CLI through one event-loop boundary."""
|
|
383
|
+
return asyncio.run(main())
|
|
384
|
+
|
|
385
|
+
|
|
386
|
+
if __name__ == "__main__":
|
|
387
|
+
raise SystemExit(console_main())
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# Checkpoints metadata allow-list
|
|
2
|
+
|
|
3
|
+
| SDK Path | Status | Rationale |
|
|
4
|
+
| --- | --- | --- |
|
|
5
|
+
| `checkpoints.record.get` | PERMITTED | Single record metadata |
|
|
6
|
+
| `checkpoints.record.get_batch` | PERMITTED | Batch record metadata |
|
|
7
|
+
| `checkpoints.record.search` | PERMITTED | Record search metadata |
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Foundry Checkpoints CLI scripts package."""
|