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.
Files changed (89) hide show
  1. pal_found_cli/__init__.py +1 -0
  2. pal_found_cli/_version.py +24 -0
  3. pal_found_cli/admin/__init__.py +2 -0
  4. pal_found_cli/admin/scripts/__init__.py +2 -0
  5. pal_found_cli/admin/scripts/pal_found_admin_cli.py +600 -0
  6. pal_found_cli/aip_agents/__init__.py +1 -0
  7. pal_found_cli/aip_agents/metadata-allow-list.md +20 -0
  8. pal_found_cli/aip_agents/scripts/__init__.py +1 -0
  9. pal_found_cli/aip_agents/scripts/pal_found_aip_agents_cli.py +704 -0
  10. pal_found_cli/audit/__init__.py +1 -0
  11. pal_found_cli/audit/metadata-allow-list.md +4 -0
  12. pal_found_cli/audit/scripts/__init__.py +1 -0
  13. pal_found_cli/audit/scripts/pal_found_audit_cli.py +387 -0
  14. pal_found_cli/checkpoints/__init__.py +9 -0
  15. pal_found_cli/checkpoints/metadata-allow-list.md +7 -0
  16. pal_found_cli/checkpoints/scripts/__init__.py +1 -0
  17. pal_found_cli/checkpoints/scripts/pal_found_checkpoints_cli.py +527 -0
  18. pal_found_cli/common/__init__.py +56 -0
  19. pal_found_cli/common/access_control_guard.py +591 -0
  20. pal_found_cli/common/async_client_factory.py +161 -0
  21. pal_found_cli/common/auth_provider.py +82 -0
  22. pal_found_cli/common/binary_download_handler.py +288 -0
  23. pal_found_cli/common/config_loader.py +388 -0
  24. pal_found_cli/common/error_serializer.py +412 -0
  25. pal_found_cli/common/log_setup.py +241 -0
  26. pal_found_cli/common/metadata-allow-list.md +169 -0
  27. pal_found_cli/common/output_formatter.py +267 -0
  28. pal_found_cli/common/pagination_helper.py +212 -0
  29. pal_found_cli/common/retry.py +617 -0
  30. pal_found_cli/common/sdk_error_utils.py +112 -0
  31. pal_found_cli/common/session_manager.py +461 -0
  32. pal_found_cli/common/tracing_provider.py +107 -0
  33. pal_found_cli/connectivity/__init__.py +9 -0
  34. pal_found_cli/connectivity/metadata-allow-list.md +24 -0
  35. pal_found_cli/connectivity/scripts/__init__.py +1 -0
  36. pal_found_cli/connectivity/scripts/pal_found_connectivity_cli.py +720 -0
  37. pal_found_cli/data_health/__init__.py +9 -0
  38. pal_found_cli/data_health/metadata-allow-list.md +10 -0
  39. pal_found_cli/data_health/scripts/__init__.py +1 -0
  40. pal_found_cli/data_health/scripts/pal_found_data_health_cli.py +458 -0
  41. pal_found_cli/datasets/__init__.py +1 -0
  42. pal_found_cli/datasets/scripts/__init__.py +1 -0
  43. pal_found_cli/datasets/scripts/pal_found_datasets_cli.py +732 -0
  44. pal_found_cli/filesystem/__init__.py +2 -0
  45. pal_found_cli/filesystem/scripts/__init__.py +2 -0
  46. pal_found_cli/filesystem/scripts/pal_found_filesystem_cli.py +659 -0
  47. pal_found_cli/functions/__init__.py +2 -0
  48. pal_found_cli/functions/scripts/__init__.py +2 -0
  49. pal_found_cli/functions/scripts/pal_found_functions_cli.py +389 -0
  50. pal_found_cli/language_models/__init__.py +1 -0
  51. pal_found_cli/language_models/metadata-allow-list.md +6 -0
  52. pal_found_cli/language_models/scripts/__init__.py +1 -0
  53. pal_found_cli/language_models/scripts/pal_found_language_models_cli.py +342 -0
  54. pal_found_cli/media_sets/__init__.py +9 -0
  55. pal_found_cli/media_sets/metadata-allow-list.md +23 -0
  56. pal_found_cli/media_sets/scripts/__init__.py +1 -0
  57. pal_found_cli/media_sets/scripts/pal_found_media_sets_cli.py +642 -0
  58. pal_found_cli/models/__init__.py +5 -0
  59. pal_found_cli/models/metadata-allow-list.md +27 -0
  60. pal_found_cli/models/scripts/__init__.py +1 -0
  61. pal_found_cli/models/scripts/pal_found_models_cli.py +714 -0
  62. pal_found_cli/ontologies/__init__.py +1 -0
  63. pal_found_cli/ontologies/scripts/__init__.py +1 -0
  64. pal_found_cli/ontologies/scripts/pal_found_ontologies_cli.py +1225 -0
  65. pal_found_cli/orchestration/__init__.py +9 -0
  66. pal_found_cli/orchestration/metadata-allow-list.md +24 -0
  67. pal_found_cli/orchestration/scripts/__init__.py +1 -0
  68. pal_found_cli/orchestration/scripts/pal_found_orchestration_cli.py +640 -0
  69. pal_found_cli/sql_queries/__init__.py +9 -0
  70. pal_found_cli/sql_queries/metadata-allow-list.md +9 -0
  71. pal_found_cli/sql_queries/scripts/__init__.py +1 -0
  72. pal_found_cli/sql_queries/scripts/pal_found_sql_queries_cli.py +514 -0
  73. pal_found_cli/streams/__init__.py +9 -0
  74. pal_found_cli/streams/metadata-allow-list.md +19 -0
  75. pal_found_cli/streams/scripts/__init__.py +1 -0
  76. pal_found_cli/streams/scripts/pal_found_streams_cli.py +615 -0
  77. pal_found_cli/third_party_applications/__init__.py +9 -0
  78. pal_found_cli/third_party_applications/metadata-allow-list.md +13 -0
  79. pal_found_cli/third_party_applications/scripts/__init__.py +1 -0
  80. pal_found_cli/third_party_applications/scripts/pal_found_third_party_applications_cli.py +547 -0
  81. pal_found_cli/widgets/__init__.py +9 -0
  82. pal_found_cli/widgets/metadata-allow-list.md +12 -0
  83. pal_found_cli/widgets/scripts/__init__.py +1 -0
  84. pal_found_cli/widgets/scripts/pal_found_widgets_cli.py +571 -0
  85. pal_found_cli-0.1.2.dist-info/METADATA +74 -0
  86. pal_found_cli-0.1.2.dist-info/RECORD +89 -0
  87. pal_found_cli-0.1.2.dist-info/WHEEL +5 -0
  88. pal_found_cli-0.1.2.dist-info/entry_points.txt +19 -0
  89. pal_found_cli-0.1.2.dist-info/top_level.txt +1 -0
@@ -0,0 +1,2 @@
1
+ """Foundry Filesystem CLI package."""
2
+
@@ -0,0 +1,2 @@
1
+ """Executable scripts for Foundry Filesystem CLI."""
2
+
@@ -0,0 +1,659 @@
1
+ #!/usr/bin/env python3
2
+ """Foundry Filesystem CLI - 31 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 (
33
+ AccessControlError,
34
+ AccessControlGuard,
35
+ )
36
+ from pal_found_cli.common.async_client_factory import AsyncClientFactory
37
+ from pal_found_cli.common.config_loader import ConfigLoader
38
+ from pal_found_cli.common.error_serializer import (
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
50
+ from pal_found_cli.common.output_formatter import OutputFormatter
51
+ from pal_found_cli.common.pagination_helper import PaginationHelper
52
+ from pal_found_cli.common.retry import RetryHandler
53
+
54
+ logger = logging.getLogger(__name__)
55
+
56
+ OperationSpec = dict[str, Any]
57
+
58
+
59
+ def _op(
60
+ resource: str,
61
+ operation: str,
62
+ client_path: str,
63
+ method: str,
64
+ positional: Iterable[str] = (),
65
+ required: Iterable[str] = (),
66
+ optional: Iterable[str] = (),
67
+ *,
68
+ json_args: Iterable[str] = (),
69
+ ) -> OperationSpec:
70
+ """Build an operation specification entry."""
71
+ return {
72
+ "resource": resource,
73
+ "operation": operation,
74
+ "client_path": client_path,
75
+ "method": method,
76
+ "positional": tuple(positional),
77
+ "required": tuple(required),
78
+ "optional": tuple(optional),
79
+ "json_args": frozenset(json_args),
80
+ }
81
+
82
+
83
+ OP_SPECS: tuple[OperationSpec, ...] = (
84
+ _op(
85
+ "folder",
86
+ "children",
87
+ "Folder",
88
+ "children",
89
+ ("folder_rid",),
90
+ optional=("page_size", "page_token"),
91
+ ),
92
+ _op(
93
+ "folder",
94
+ "create",
95
+ "Folder",
96
+ "create",
97
+ required=("display_name", "parent_folder_rid"),
98
+ ),
99
+ _op("folder", "get", "Folder", "get", ("folder_rid",)),
100
+ _op(
101
+ "folder",
102
+ "get_batch",
103
+ "Folder",
104
+ "get_batch",
105
+ ("body",),
106
+ json_args=("body",),
107
+ ),
108
+ _op(
109
+ "folder",
110
+ "replace",
111
+ "Folder",
112
+ "replace",
113
+ ("folder_rid",),
114
+ required=("display_name", "parent_folder_rid"),
115
+ optional=("preview",),
116
+ ),
117
+ _op(
118
+ "project",
119
+ "add_organizations",
120
+ "Project",
121
+ "add_organizations",
122
+ ("project_rid",),
123
+ required=("organization_rids",),
124
+ json_args=("organization_rids",),
125
+ ),
126
+ _op(
127
+ "project",
128
+ "create",
129
+ "Project",
130
+ "create",
131
+ required=(
132
+ "default_roles",
133
+ "display_name",
134
+ "organization_rids",
135
+ "role_grants",
136
+ "space_rid",
137
+ ),
138
+ optional=("description", "resource_level_role_grants_allowed"),
139
+ json_args=("default_roles", "organization_rids", "role_grants"),
140
+ ),
141
+ _op(
142
+ "project",
143
+ "create_from_template",
144
+ "Project",
145
+ "create_from_template",
146
+ required=("template_rid", "variable_values"),
147
+ optional=("default_roles", "organization_rids", "project_description"),
148
+ json_args=("variable_values", "default_roles", "organization_rids"),
149
+ ),
150
+ _op("project", "get", "Project", "get", ("project_rid",)),
151
+ _op(
152
+ "project",
153
+ "organizations",
154
+ "Project",
155
+ "organizations",
156
+ ("project_rid",),
157
+ optional=("page_size", "page_token"),
158
+ ),
159
+ _op(
160
+ "project",
161
+ "remove_organizations",
162
+ "Project",
163
+ "remove_organizations",
164
+ ("project_rid",),
165
+ required=("organization_rids",),
166
+ json_args=("organization_rids",),
167
+ ),
168
+ _op(
169
+ "project",
170
+ "replace",
171
+ "Project",
172
+ "replace",
173
+ ("project_rid",),
174
+ required=("display_name",),
175
+ optional=("description", "preview"),
176
+ ),
177
+ _op(
178
+ "resource",
179
+ "add_markings",
180
+ "Resource",
181
+ "add_markings",
182
+ ("resource_rid",),
183
+ required=("marking_ids",),
184
+ json_args=("marking_ids",),
185
+ ),
186
+ _op("resource", "delete", "Resource", "delete", ("resource_rid",)),
187
+ _op("resource", "get", "Resource", "get", ("resource_rid",)),
188
+ _op(
189
+ "resource",
190
+ "get_access_requirements",
191
+ "Resource",
192
+ "get_access_requirements",
193
+ ("resource_rid",),
194
+ ),
195
+ _op(
196
+ "resource",
197
+ "get_batch",
198
+ "Resource",
199
+ "get_batch",
200
+ ("body",),
201
+ json_args=("body",),
202
+ ),
203
+ _op(
204
+ "resource",
205
+ "get_by_path",
206
+ "Resource",
207
+ "get_by_path",
208
+ required=("path",),
209
+ ),
210
+ _op(
211
+ "resource",
212
+ "get_by_path_batch",
213
+ "Resource",
214
+ "get_by_path_batch",
215
+ ("body",),
216
+ json_args=("body",),
217
+ ),
218
+ _op(
219
+ "resource",
220
+ "markings",
221
+ "Resource",
222
+ "markings",
223
+ ("resource_rid",),
224
+ optional=("page_size", "page_token"),
225
+ ),
226
+ _op(
227
+ "resource",
228
+ "permanently_delete",
229
+ "Resource",
230
+ "permanently_delete",
231
+ ("resource_rid",),
232
+ ),
233
+ _op(
234
+ "resource",
235
+ "remove_markings",
236
+ "Resource",
237
+ "remove_markings",
238
+ ("resource_rid",),
239
+ required=("marking_ids",),
240
+ json_args=("marking_ids",),
241
+ ),
242
+ _op("resource", "restore", "Resource", "restore", ("resource_rid",)),
243
+ _op(
244
+ "resource_role",
245
+ "add",
246
+ "Resource.Role",
247
+ "add",
248
+ ("resource_rid",),
249
+ required=("roles",),
250
+ json_args=("roles",),
251
+ ),
252
+ _op(
253
+ "resource_role",
254
+ "list",
255
+ "Resource.Role",
256
+ "list",
257
+ ("resource_rid",),
258
+ optional=("include_inherited", "page_size", "page_token"),
259
+ ),
260
+ _op(
261
+ "resource_role",
262
+ "remove",
263
+ "Resource.Role",
264
+ "remove",
265
+ ("resource_rid",),
266
+ required=("roles",),
267
+ json_args=("roles",),
268
+ ),
269
+ _op(
270
+ "space",
271
+ "create",
272
+ "Space",
273
+ "create",
274
+ required=(
275
+ "deletion_policy_organizations",
276
+ "display_name",
277
+ "enrollment_rid",
278
+ "organizations",
279
+ ),
280
+ optional=(
281
+ "default_role_set_id",
282
+ "description",
283
+ "file_system_id",
284
+ "preview",
285
+ "usage_account_rid",
286
+ ),
287
+ json_args=("deletion_policy_organizations", "organizations"),
288
+ ),
289
+ _op("space", "delete", "Space", "delete", ("space_rid",), optional=("preview",)),
290
+ _op("space", "get", "Space", "get", ("space_rid",), optional=("preview",)),
291
+ _op("space", "list", "Space", "list", optional=("page_size", "page_token")),
292
+ _op(
293
+ "space",
294
+ "replace",
295
+ "Space",
296
+ "replace",
297
+ ("space_rid",),
298
+ required=("display_name",),
299
+ optional=("default_role_set_id", "description", "preview", "usage_account_rid"),
300
+ ),
301
+ )
302
+
303
+ OPERATION_BY_RESOURCE: dict[str, dict[str, OperationSpec]] = {}
304
+ for _spec in OP_SPECS:
305
+ OPERATION_BY_RESOURCE.setdefault(_spec["resource"], {})[_spec["operation"]] = _spec
306
+
307
+ PAGINATED_OPS = frozenset(
308
+ {
309
+ ("folder", "children"),
310
+ ("project", "organizations"),
311
+ ("resource", "markings"),
312
+ ("resource_role", "list"),
313
+ ("space", "list"),
314
+ }
315
+ )
316
+
317
+
318
+ def _model_to_dict(obj: Any) -> Any:
319
+ """Convert SDK/Pydantic objects into JSON-serializable values."""
320
+ if obj is None:
321
+ return None
322
+ if isinstance(obj, (bytes, bytearray)):
323
+ return {"bytes": len(obj)}
324
+ if isinstance(obj, list):
325
+ return [_model_to_dict(item) for item in obj]
326
+ if isinstance(obj, dict):
327
+ return {key: _model_to_dict(value) for key, value in obj.items()}
328
+ try:
329
+ result = obj.to_dict()
330
+ if isinstance(result, dict):
331
+ return result
332
+ except (AttributeError, TypeError):
333
+ pass
334
+ try:
335
+ result = obj.model_dump()
336
+ if isinstance(result, dict):
337
+ return result
338
+ except (AttributeError, TypeError):
339
+ pass
340
+ try:
341
+ result = obj.dict()
342
+ if isinstance(result, dict):
343
+ return result
344
+ except (AttributeError, TypeError):
345
+ pass
346
+ return obj
347
+
348
+
349
+ def _resolve(resource: str, op: str) -> str:
350
+ """Resolve CLI kebab-case operation name to SDK snake_case."""
351
+ return op.replace("-", "_")
352
+
353
+
354
+ def _kebab(name: str) -> str:
355
+ """Convert snake_case names to CLI kebab-case."""
356
+ return name.replace("_", "-")
357
+
358
+
359
+ def _common_parser() -> argparse.ArgumentParser:
360
+ """Build shared parser for operation-level common options."""
361
+ common = argparse.ArgumentParser(add_help=False)
362
+ common.add_argument("--timeout", type=int, default=None)
363
+ common.add_argument("--format", choices=["json", "toon", "auto"], default="auto")
364
+ common.add_argument("--pretty", action="store_true")
365
+ common.add_argument("--page-size", type=int, default=None, dest="page_size")
366
+ common.add_argument("--page-token", type=str, default=None, dest="page_token")
367
+ common.add_argument("--batch-pages", type=int, default=None, dest="batch_pages")
368
+ return common
369
+
370
+
371
+ def _add_kwarg(
372
+ parser: argparse.ArgumentParser, arg_name: str, *, required: bool
373
+ ) -> None:
374
+ flag = "--" + _kebab(arg_name)
375
+ if arg_name in {
376
+ "include_inherited",
377
+ "preview",
378
+ "resource_level_role_grants_allowed",
379
+ }:
380
+ parser.add_argument(flag, action="store_true", default=None, dest=arg_name)
381
+ return
382
+ parser.add_argument(flag, required=required, default=None, dest=arg_name)
383
+
384
+
385
+ def build_parser() -> argparse.ArgumentParser:
386
+ """Build argparse parser with all 31 filesystem operations."""
387
+ parser = argparse.ArgumentParser(
388
+ prog="pal_found_filesystem_cli",
389
+ description="Foundry Filesystem CLI - 31 operations",
390
+ )
391
+ subparsers = parser.add_subparsers(dest="resource", help="Resource type")
392
+
393
+ for resource in sorted(OPERATION_BY_RESOURCE):
394
+ res_parser = subparsers.add_parser(_kebab(resource))
395
+ op_sub = res_parser.add_subparsers(dest="operation")
396
+ for operation, spec in sorted(OPERATION_BY_RESOURCE[resource].items()):
397
+ op_parser = op_sub.add_parser(_kebab(operation), parents=[_common_parser()])
398
+ for arg_name in spec["positional"]:
399
+ op_parser.add_argument(arg_name)
400
+ for arg_name in spec["required"]:
401
+ _add_kwarg(op_parser, arg_name, required=True)
402
+ for arg_name in spec["optional"]:
403
+ if arg_name in {"page_size", "page_token"}:
404
+ continue
405
+ _add_kwarg(op_parser, arg_name, required=False)
406
+
407
+ return parser
408
+
409
+
410
+ def _spec_for(resource: str, operation: str) -> OperationSpec:
411
+ """Return the operation spec for a resource and operation."""
412
+ try:
413
+ return OPERATION_BY_RESOURCE[resource][operation]
414
+ except KeyError as exc:
415
+ raise ValueError(f"Unknown operation: {resource}.{operation}") from exc
416
+
417
+
418
+ def _get_client(
419
+ cfg: ConfigLoader, resource: str, factory: AsyncClientFactory | None = None
420
+ ) -> Any:
421
+ """Get SDK client for a filesystem resource."""
422
+ root = (factory or AsyncClientFactory()).create(cfg).filesystem
423
+ spec = next(iter(OPERATION_BY_RESOURCE[resource].values()))
424
+ client = root
425
+ for attr in spec["client_path"].split("."):
426
+ client = getattr(client, attr)
427
+ return client
428
+
429
+
430
+ def _coerce_arg(value: Any, *, json_arg: bool) -> Any:
431
+ """Parse JSON CLI values for structured SDK arguments."""
432
+ if value is None:
433
+ return None
434
+ if json_arg and isinstance(value, str):
435
+ return json.loads(value)
436
+ return value
437
+
438
+
439
+ async def _resolve_result(value: Any) -> Any:
440
+ """Await SDK calls that return awaitables."""
441
+ if asyncio.isfuture(value) or inspect.isawaitable(value):
442
+ return await value
443
+ return value
444
+
445
+
446
+ def _is_async_iterator(value: Any) -> bool:
447
+ """Return true for SDK async resource iterators."""
448
+ return hasattr(value, "__aiter__") and hasattr(value, "__anext__")
449
+
450
+
451
+ async def _read_next_page_token(iterator: Any) -> str | None:
452
+ """Read the SDK async iterator cursor when the implementation exposes it."""
453
+ page_iterator = getattr(iterator, "_page_iterator", None)
454
+ token_source = page_iterator if page_iterator is not None else iterator
455
+ getter = getattr(token_source, "get_next_page_token", None)
456
+ if callable(getter):
457
+ token = await _resolve_result(getter())
458
+ return token if isinstance(token, str) else None
459
+ token = getattr(token_source, "next_page_token", None)
460
+ return token if isinstance(token, str) else None
461
+
462
+
463
+ async def _collect_async_iterator(iterator: Any, helper: PaginationHelper) -> list[Any]:
464
+ """Collect items from a Foundry SDK AsyncResourceIterator."""
465
+ items: list[Any] = []
466
+ item_limit = helper.page_size * helper.batch_pages
467
+
468
+ async for item in iterator:
469
+ items.append(item)
470
+ if len(items) >= item_limit:
471
+ break
472
+
473
+ helper._total_items += len(items)
474
+ helper._pages_fetched += max(1, math.ceil(len(items) / helper.page_size))
475
+ helper._next_page_token = await _read_next_page_token(iterator)
476
+ return items
477
+
478
+
479
+ async def _paginate_page_envelopes(
480
+ first_response: Any,
481
+ call_func: Any,
482
+ helper: PaginationHelper,
483
+ ) -> list[Any]:
484
+ """Collect dict/list page envelopes used by older tests and some SDK methods."""
485
+ all_items = helper._extract_items(first_response)
486
+ helper._total_items += len(all_items)
487
+ helper._pages_fetched += 1
488
+ token = helper._extract_next_token(first_response)
489
+
490
+ while token is not None and helper.pages_fetched < helper.batch_pages:
491
+ response = await call_func(page_size=helper.page_size, page_token=token)
492
+ items = helper._extract_items(response)
493
+ all_items.extend(items)
494
+ helper._total_items += len(items)
495
+ helper._pages_fetched += 1
496
+ token = helper._extract_next_token(response)
497
+
498
+ helper._next_page_token = token
499
+ return all_items
500
+
501
+
502
+ async def _invoke(
503
+ resource: str,
504
+ operation: str,
505
+ client: Any,
506
+ args: argparse.Namespace,
507
+ timeout: int | None,
508
+ ) -> Any:
509
+ """Invoke a filesystem SDK operation."""
510
+ spec = _spec_for(resource, operation)
511
+ method = getattr(client, spec["method"])
512
+ positional = [getattr(args, name) for name in spec["positional"]]
513
+ kwargs: dict[str, Any] = {}
514
+
515
+ for name in (*spec["required"], *spec["optional"]):
516
+ if name in {"page_size", "page_token"}:
517
+ value = getattr(args, name, None)
518
+ else:
519
+ value = getattr(args, name, None)
520
+ if value is not None:
521
+ kwargs[name] = _coerce_arg(value, json_arg=name in spec["json_args"])
522
+
523
+ kwargs["request_timeout"] = timeout
524
+ return await _resolve_result(method(*positional, **kwargs))
525
+
526
+
527
+ def _is_paginated(resource: str, operation: str) -> bool:
528
+ """Return true if operation supports SDK pagination args."""
529
+ return (resource, operation) in PAGINATED_OPS
530
+
531
+
532
+ async def _invoke_paginated(
533
+ resource: str,
534
+ operation: str,
535
+ client: Any,
536
+ args: argparse.Namespace,
537
+ timeout: int | None,
538
+ helper: PaginationHelper,
539
+ ) -> Any:
540
+ """Invoke a paginated filesystem operation."""
541
+
542
+ async def _single_page(**page_kwargs: Any) -> Any:
543
+ paged_args = argparse.Namespace(**vars(args))
544
+ paged_args.page_size = page_kwargs.get(
545
+ "page_size", getattr(args, "page_size", None)
546
+ )
547
+ paged_args.page_token = page_kwargs.get("page_token", None)
548
+ return await _invoke(resource, operation, client, paged_args, timeout)
549
+
550
+ first_response = await _single_page(**helper.get_sdk_params())
551
+ if _is_async_iterator(first_response):
552
+ return await _collect_async_iterator(first_response, helper)
553
+ return await _paginate_page_envelopes(first_response, _single_page, helper)
554
+
555
+
556
+ async def main() -> int:
557
+ """Run the Foundry Filesystem CLI."""
558
+ parser = build_parser()
559
+ args = parser.parse_args()
560
+ if not args.resource:
561
+ parser.print_help()
562
+ return EXIT_USER_INPUT
563
+ if not getattr(args, "operation", None):
564
+ parser.print_help()
565
+ return EXIT_USER_INPUT
566
+
567
+ cfg = ConfigLoader()
568
+ cfg.load()
569
+ LogSetup.configure(log_level=cfg.log_level)
570
+
571
+ resource = args.resource.replace("-", "_")
572
+ operation = _resolve(resource, args.operation)
573
+ logger.info(
574
+ "Executing operation", extra={"resource": resource, "operation": operation}
575
+ )
576
+
577
+ try:
578
+ _spec_for(resource, operation)
579
+ except ValueError as exc:
580
+ ErrorSerializer().serialize(exc)
581
+ return EXIT_USER_INPUT
582
+
583
+ try:
584
+ AccessControlGuard(cfg, "FILESYSTEM").check(resource, operation)
585
+ except AccessControlError as exc:
586
+ ErrorSerializer().serialize(exc)
587
+ return EXIT_ACCESS_CONTROL
588
+
589
+ factory = AsyncClientFactory()
590
+ try:
591
+ with factory.invocation_scope(cfg):
592
+ client = _get_client(cfg, resource, factory)
593
+ timeout = getattr(args, "timeout", None) or getattr(cfg, "timeout_s", None)
594
+ retry_handler = RetryHandler()
595
+ helper: PaginationHelper | None = None
596
+ if _is_paginated(resource, operation):
597
+ helper = PaginationHelper(
598
+ page_size=getattr(args, "page_size", None),
599
+ page_token=getattr(args, "page_token", None),
600
+ batch_pages=getattr(args, "batch_pages", None),
601
+ )
602
+ if helper is not None:
603
+ result = await retry_handler.execute(
604
+ _invoke_paginated,
605
+ resource,
606
+ operation,
607
+ client,
608
+ args,
609
+ timeout,
610
+ helper,
611
+ )
612
+ else:
613
+ result = await retry_handler.execute(
614
+ _invoke,
615
+ resource,
616
+ operation,
617
+ client,
618
+ args,
619
+ timeout,
620
+ )
621
+ formatter = OutputFormatter(
622
+ format_setting=getattr(args, "format", "auto"),
623
+ pretty=getattr(args, "pretty", False),
624
+ )
625
+ print(formatter.format(_model_to_dict(result)))
626
+ if helper is not None:
627
+ helper.emit_metadata()
628
+ return EXIT_SUCCESS
629
+ except PermissionError as exc:
630
+ ErrorSerializer().serialize(exc)
631
+ return EXIT_PERMISSION_DENIED
632
+ except FileNotFoundError as exc:
633
+ ErrorSerializer().serialize(exc)
634
+ return EXIT_NOT_FOUND
635
+ except TimeoutError as exc:
636
+ ErrorSerializer().serialize(exc)
637
+ return EXIT_TIMEOUT
638
+ except ValueError as exc:
639
+ ErrorSerializer().serialize(exc)
640
+ return EXIT_USER_INPUT
641
+ except OSError as exc:
642
+ exit_code = ErrorSerializer().serialize(exc)
643
+ if getattr(exc, "errno", None) in (11, 115):
644
+ return EXIT_RATE_LIMIT
645
+ return exit_code
646
+ except Exception as exc:
647
+ exit_code = ErrorSerializer().serialize(exc)
648
+ if exit_code != EXIT_USER_INPUT:
649
+ return exit_code
650
+ return EXIT_SERVER_ERROR
651
+
652
+
653
+ def console_main() -> int:
654
+ """Run the async CLI from the console script entry point."""
655
+ return asyncio.run(main())
656
+
657
+
658
+ if __name__ == "__main__":
659
+ sys.exit(console_main())
@@ -0,0 +1,2 @@
1
+ """Foundry Functions namespace CLI package."""
2
+
@@ -0,0 +1,2 @@
1
+ """Console scripts for the Foundry Functions namespace."""
2
+