cu-cli-core 0.1.0b1__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.
@@ -0,0 +1,1099 @@
1
+ # Copyright (c) Microsoft Corporation.
2
+ # Licensed under the MIT license.
3
+
4
+ """Framework-neutral metadata for the shared CU command surface."""
5
+
6
+ from __future__ import annotations
7
+
8
+ from dataclasses import dataclass
9
+ from enum import Enum
10
+ from functools import lru_cache
11
+ import importlib
12
+ from typing import Any, Mapping
13
+
14
+
15
+ class SurfaceClassification(str, Enum):
16
+ """Identify which frontend owns a command-surface element."""
17
+
18
+ COMMON = "common"
19
+ SHARED_ALIAS = "shared-alias"
20
+ STANDALONE_SHORTCUT = "standalone-shortcut"
21
+ AZURE_HOST_GLOBAL = "azure-host-global"
22
+ FRONTEND_PRESENTATION = "frontend-presentation"
23
+
24
+
25
+ class ArgumentValueType(str, Enum):
26
+ """Portable value types understood by frontend adapters."""
27
+
28
+ STRING = "string"
29
+ BOOLEAN = "boolean"
30
+ INTEGER = "integer"
31
+ PATH = "path"
32
+
33
+
34
+ @dataclass(frozen=True)
35
+ class ArgumentSpec:
36
+ """Describe one canonical argument or frontend-specific overlay."""
37
+
38
+ name: str
39
+ field: str
40
+ parser_name: str
41
+ help: str
42
+ value_type: ArgumentValueType = ArgumentValueType.STRING
43
+ aliases: tuple[str, ...] = ()
44
+ required: bool = False
45
+ default: Any = None
46
+ choices: tuple[str, ...] = ()
47
+ repeatable: bool = False
48
+ minimum: int | None = None
49
+ maximum: int | None = None
50
+ path_exists: bool = False
51
+ file_okay: bool = True
52
+ dir_okay: bool = True
53
+ metavar: str | None = None
54
+ classification: SurfaceClassification = SurfaceClassification.COMMON
55
+
56
+ @property
57
+ def positional(self) -> bool:
58
+ return not self.name.startswith("-")
59
+
60
+
61
+ @dataclass(frozen=True)
62
+ class CommandSpec:
63
+ """Describe one command without importing either CLI framework or operation."""
64
+
65
+ path: tuple[str, ...]
66
+ help: str
67
+ operation: str
68
+ request_type: str
69
+ arguments: tuple[ArgumentSpec, ...] = ()
70
+ service_options: tuple[str, ...] = ()
71
+ classification: SurfaceClassification = SurfaceClassification.COMMON
72
+ preview: bool = False
73
+ deprecated: bool = False
74
+
75
+
76
+ class CommandBindingError(ValueError):
77
+ """Raised when parsed frontend values cannot form a canonical request."""
78
+
79
+
80
+ @lru_cache(maxsize=None)
81
+ def resolve_identifier(identifier: str) -> Any:
82
+ """Resolve a lazy ``module#attribute`` identifier on first use."""
83
+
84
+ module_name, separator, attribute_name = identifier.partition("#")
85
+ if not separator or not module_name or not attribute_name:
86
+ raise ValueError(f"invalid lazy identifier: {identifier!r}")
87
+ module = importlib.import_module(module_name)
88
+ return getattr(module, attribute_name)
89
+
90
+
91
+ def bind_command_arguments(
92
+ spec: CommandSpec,
93
+ parsed: Mapping[str, Any],
94
+ ) -> dict[str, Any]:
95
+ """Bind canonical and overlay parser values to shared request fields."""
96
+
97
+ bound: dict[str, Any] = {}
98
+ sources: dict[str, str] = {}
99
+ required: dict[str, str] = {}
100
+
101
+ for argument in spec.arguments:
102
+ if argument.classification in {
103
+ SurfaceClassification.AZURE_HOST_GLOBAL,
104
+ SurfaceClassification.FRONTEND_PRESENTATION,
105
+ }:
106
+ continue
107
+ if argument.required:
108
+ required[argument.field] = argument.name
109
+ value = parsed.get(argument.parser_name, argument.default)
110
+ if value is None or (argument.repeatable and not value):
111
+ continue
112
+ if argument.field in bound:
113
+ raise CommandBindingError(
114
+ f"provide {argument.field.replace('_', ' ')} only once; "
115
+ f"{sources[argument.field]} cannot be combined with {argument.name}."
116
+ )
117
+ bound[argument.field] = value
118
+ sources[argument.field] = argument.name
119
+
120
+ for field, argument_name in required.items():
121
+ if field not in bound:
122
+ raise CommandBindingError(f"missing required argument: {argument_name}.")
123
+
124
+ return bound
125
+
126
+
127
+ def build_request(spec: CommandSpec, parsed: Mapping[str, Any]) -> Any:
128
+ """Bind frontend values and instantiate the spec's lazy request type."""
129
+
130
+ request_type = resolve_identifier(spec.request_type)
131
+ try:
132
+ return request_type(**bind_command_arguments(spec, parsed))
133
+ except (TypeError, ValueError) as exc:
134
+ if isinstance(exc, CommandBindingError):
135
+ raise
136
+ raise CommandBindingError(str(exc)) from exc
137
+
138
+
139
+ _SERVICE_OPTIONS = ("endpoint", "api-version", "auth-mode", "api-key")
140
+
141
+
142
+ def _profile_name_arguments(option_help: str) -> tuple[ArgumentSpec, ...]:
143
+ return (
144
+ ArgumentSpec(
145
+ "--name",
146
+ aliases=("-n",),
147
+ field="name",
148
+ parser_name="profile_name",
149
+ help=option_help,
150
+ required=True,
151
+ ),
152
+ ArgumentSpec(
153
+ "PROFILE_NAME",
154
+ field="name",
155
+ parser_name="positional_profile_name",
156
+ help="Standalone positional shortcut for --name.",
157
+ classification=SurfaceClassification.STANDALONE_SHORTCUT,
158
+ ),
159
+ )
160
+
161
+
162
+ _ANALYZER_NAME_ARGUMENTS = (
163
+ ArgumentSpec(
164
+ "--name",
165
+ aliases=("-n", "-a"),
166
+ field="name",
167
+ parser_name="analyzer_name",
168
+ help="Analyzer name.",
169
+ required=True,
170
+ ),
171
+ ArgumentSpec(
172
+ "ANALYZER_NAME",
173
+ field="name",
174
+ parser_name="positional_analyzer_name",
175
+ help="Standalone positional shortcut for --name.",
176
+ classification=SurfaceClassification.STANDALONE_SHORTCUT,
177
+ ),
178
+ )
179
+
180
+ _INPUT_ARGUMENTS = (
181
+ ArgumentSpec(
182
+ "INPUTS",
183
+ field="positional_inputs",
184
+ parser_name="inputs",
185
+ help="Standalone positional file and directory shortcuts.",
186
+ value_type=ArgumentValueType.PATH,
187
+ repeatable=True,
188
+ classification=SurfaceClassification.STANDALONE_SHORTCUT,
189
+ ),
190
+ ArgumentSpec(
191
+ "--file",
192
+ field="files",
193
+ parser_name="files",
194
+ help="Literal local file. Repeat for multiple files.",
195
+ value_type=ArgumentValueType.PATH,
196
+ repeatable=True,
197
+ file_okay=True,
198
+ dir_okay=False,
199
+ ),
200
+ ArgumentSpec(
201
+ "--source",
202
+ field="sources",
203
+ parser_name="sources",
204
+ help="Local source directory. Repeat for multiple directories.",
205
+ value_type=ArgumentValueType.PATH,
206
+ repeatable=True,
207
+ file_okay=False,
208
+ dir_okay=True,
209
+ ),
210
+ ArgumentSpec(
211
+ "--pattern",
212
+ field="pattern",
213
+ parser_name="pattern",
214
+ help="Python fnmatch pattern applied to every --source.",
215
+ ),
216
+ ArgumentSpec(
217
+ "--recursive",
218
+ aliases=("-r",),
219
+ field="recursive",
220
+ parser_name="recursive",
221
+ help="Recurse into selected directories.",
222
+ value_type=ArgumentValueType.BOOLEAN,
223
+ ),
224
+ )
225
+
226
+
227
+ ANALYZE = CommandSpec(
228
+ path=("analyze",),
229
+ help="Process local files with an analyzer and return analyzer results.",
230
+ operation="cu_cli_core.operations.analysis#execute_analyze",
231
+ request_type="cu_cli_core.contracts#AnalyzeRequest",
232
+ arguments=(
233
+ *_INPUT_ARGUMENTS,
234
+ ArgumentSpec(
235
+ "--analyzer",
236
+ aliases=("-a",),
237
+ field="analyzer",
238
+ parser_name="analyzer_id",
239
+ help="Analyzer name; defaults to the configured default analyzer.",
240
+ ),
241
+ ArgumentSpec(
242
+ "--inline",
243
+ aliases=("-i",),
244
+ field="inline",
245
+ parser_name="inline",
246
+ help="Use preview synchronous analysis.",
247
+ value_type=ArgumentValueType.BOOLEAN,
248
+ ),
249
+ ArgumentSpec(
250
+ "--usage",
251
+ field="usage",
252
+ parser_name="show_usage",
253
+ help="Include service usage details.",
254
+ value_type=ArgumentValueType.BOOLEAN,
255
+ ),
256
+ ArgumentSpec(
257
+ "--llm-input",
258
+ field="llm_input",
259
+ parser_name="llm_input",
260
+ help="Return the analyzer result formatted as generative AI model input.",
261
+ value_type=ArgumentValueType.BOOLEAN,
262
+ ),
263
+ ArgumentSpec(
264
+ "--json",
265
+ field="json",
266
+ parser_name="json_output",
267
+ help="Emit the complete analyzer result as JSON.",
268
+ value_type=ArgumentValueType.BOOLEAN,
269
+ classification=SurfaceClassification.FRONTEND_PRESENTATION,
270
+ ),
271
+ ArgumentSpec(
272
+ "--output-file",
273
+ field="output_file",
274
+ parser_name="output_file",
275
+ help="Write the primary payload for one selected file.",
276
+ value_type=ArgumentValueType.PATH,
277
+ file_okay=True,
278
+ dir_okay=False,
279
+ ),
280
+ ArgumentSpec(
281
+ "--output-dir",
282
+ aliases=("-d",),
283
+ field="output_dir",
284
+ parser_name="out_dir",
285
+ help="Write results under this directory, preserving source-relative paths.",
286
+ value_type=ArgumentValueType.PATH,
287
+ file_okay=False,
288
+ dir_okay=True,
289
+ ),
290
+ ArgumentSpec(
291
+ "--on-existing",
292
+ field="on_existing",
293
+ parser_name="on_existing",
294
+ help="How to handle existing result files.",
295
+ choices=("error", "skip", "reanalyze"),
296
+ ),
297
+ ArgumentSpec(
298
+ "--dry-run",
299
+ field="dry_run",
300
+ parser_name="dry_run",
301
+ help="Display the local execution plan without service calls or writes.",
302
+ value_type=ArgumentValueType.BOOLEAN,
303
+ ),
304
+ ArgumentSpec(
305
+ "--yes",
306
+ aliases=("-y",),
307
+ field="yes",
308
+ parser_name="assume_yes",
309
+ help="Skip discovery confirmation.",
310
+ value_type=ArgumentValueType.BOOLEAN,
311
+ ),
312
+ ArgumentSpec(
313
+ "--report-file",
314
+ field="report_file",
315
+ parser_name="report_path",
316
+ help="Write a standalone JSON status report.",
317
+ value_type=ArgumentValueType.PATH,
318
+ file_okay=True,
319
+ dir_okay=False,
320
+ classification=SurfaceClassification.FRONTEND_PRESENTATION,
321
+ ),
322
+ ArgumentSpec(
323
+ "--concurrency",
324
+ aliases=("-j",),
325
+ field="concurrency",
326
+ parser_name="concurrency",
327
+ help="Concurrent analysis jobs.",
328
+ value_type=ArgumentValueType.INTEGER,
329
+ default=4,
330
+ minimum=1,
331
+ maximum=32,
332
+ ),
333
+ ),
334
+ service_options=_SERVICE_OPTIONS,
335
+ preview=True,
336
+ )
337
+
338
+
339
+ ANALYZER_SHOW = CommandSpec(
340
+ path=("analyzer", "show"),
341
+ help="Show a single analyzer definition (JSON).",
342
+ operation="cu_cli_core.operations.analyzers#get_analyzer",
343
+ request_type="cu_cli_core.contracts#AnalyzerShowRequest",
344
+ arguments=(
345
+ ArgumentSpec(
346
+ "--name",
347
+ aliases=("-n", "-a"),
348
+ field="name",
349
+ parser_name="analyzer_name",
350
+ help="Analyzer name.",
351
+ required=True,
352
+ ),
353
+ ArgumentSpec(
354
+ "ANALYZER_NAME",
355
+ field="name",
356
+ parser_name="positional_analyzer_name",
357
+ help="Standalone positional shortcut for --name.",
358
+ classification=SurfaceClassification.STANDALONE_SHORTCUT,
359
+ ),
360
+ ),
361
+ service_options=_SERVICE_OPTIONS,
362
+ )
363
+
364
+ ANALYZER_LIST = CommandSpec(
365
+ path=("analyzer", "list"),
366
+ help="List analyzers in the Microsoft Foundry resource.",
367
+ operation="cu_cli_core.operations.analyzers#list_analyzers",
368
+ request_type="cu_cli_core.contracts#AnalyzerListRequest",
369
+ arguments=(
370
+ ArgumentSpec(
371
+ "--kind",
372
+ field="kind",
373
+ parser_name="kind",
374
+ help="Filter analyzers by kind.",
375
+ default="all",
376
+ choices=("all", "prebuilt", "custom"),
377
+ ),
378
+ ArgumentSpec(
379
+ "--sort-by",
380
+ field="sort_by",
381
+ parser_name="sort_by",
382
+ help="Sort analyzers by name, creation time, or modification time.",
383
+ default="analyzerId",
384
+ choices=("analyzerId", "createdAt", "lastModifiedAt"),
385
+ ),
386
+ ArgumentSpec(
387
+ "--json",
388
+ field="json",
389
+ parser_name="json_output",
390
+ help="Write the complete result as JSON.",
391
+ value_type=ArgumentValueType.BOOLEAN,
392
+ classification=SurfaceClassification.FRONTEND_PRESENTATION,
393
+ ),
394
+ ),
395
+ service_options=_SERVICE_OPTIONS,
396
+ )
397
+
398
+ ANALYZER_CREATE = CommandSpec(
399
+ path=("analyzer", "create"),
400
+ help="Create an analyzer from a JSON schema file.",
401
+ operation="cu_cli_core.operations.analyzers#create_analyzer",
402
+ request_type="cu_cli_core.contracts#AnalyzerCreateRequest",
403
+ arguments=(
404
+ ArgumentSpec(
405
+ "--name",
406
+ aliases=("-n", "-a"),
407
+ field="name",
408
+ parser_name="analyzer_name",
409
+ help="Custom analyzer ID: 1-64 ASCII letters, numbers, or underscores.",
410
+ required=True,
411
+ ),
412
+ ArgumentSpec(
413
+ "ANALYZER_NAME",
414
+ field="name",
415
+ parser_name="positional_analyzer_name",
416
+ help="Standalone positional shortcut for --name.",
417
+ classification=SurfaceClassification.STANDALONE_SHORTCUT,
418
+ ),
419
+ ArgumentSpec(
420
+ "--schema",
421
+ aliases=("-s",),
422
+ field="schema",
423
+ parser_name="schema_path",
424
+ help="Path to a JSON analyzer schema.",
425
+ value_type=ArgumentValueType.PATH,
426
+ required=True,
427
+ ),
428
+ ),
429
+ service_options=_SERVICE_OPTIONS,
430
+ )
431
+
432
+ ANALYZER_DELETE = CommandSpec(
433
+ path=("analyzer", "delete"),
434
+ help="Delete an analyzer.",
435
+ operation="cu_cli_core.operations.analyzers#delete_analyzer",
436
+ request_type="cu_cli_core.contracts#AnalyzerDeleteRequest",
437
+ arguments=(
438
+ ArgumentSpec(
439
+ "--name",
440
+ aliases=("-n", "-a"),
441
+ field="name",
442
+ parser_name="analyzer_name",
443
+ help="Analyzer name.",
444
+ required=True,
445
+ ),
446
+ ArgumentSpec(
447
+ "ANALYZER_NAME",
448
+ field="name",
449
+ parser_name="positional_analyzer_name",
450
+ help="Standalone positional shortcut for --name.",
451
+ classification=SurfaceClassification.STANDALONE_SHORTCUT,
452
+ ),
453
+ ArgumentSpec(
454
+ "--yes",
455
+ aliases=("-y",),
456
+ field="yes",
457
+ parser_name="yes",
458
+ help="Skip confirmation.",
459
+ value_type=ArgumentValueType.BOOLEAN,
460
+ ),
461
+ ),
462
+ service_options=_SERVICE_OPTIONS,
463
+ )
464
+
465
+ ANALYZER_TEST = CommandSpec(
466
+ path=("analyzer", "test"),
467
+ help="Run an analyzer over local samples.",
468
+ operation="cu_cli_core.operations.analysis#execute_analyzer_test",
469
+ request_type="cu_cli_core.contracts#AnalyzerTestRequest",
470
+ arguments=(
471
+ *_ANALYZER_NAME_ARGUMENTS,
472
+ *_INPUT_ARGUMENTS,
473
+ ArgumentSpec(
474
+ "--dry-run",
475
+ field="dry_run",
476
+ parser_name="dry_run",
477
+ help="Display the local test plan without service calls.",
478
+ value_type=ArgumentValueType.BOOLEAN,
479
+ ),
480
+ ArgumentSpec(
481
+ "--json",
482
+ field="json",
483
+ parser_name="json_output",
484
+ help="Emit the complete structured test report.",
485
+ value_type=ArgumentValueType.BOOLEAN,
486
+ classification=SurfaceClassification.FRONTEND_PRESENTATION,
487
+ ),
488
+ ArgumentSpec(
489
+ "--output-file",
490
+ field="output_file",
491
+ parser_name="out_path",
492
+ help="Write the structured test report to a file.",
493
+ value_type=ArgumentValueType.PATH,
494
+ file_okay=True,
495
+ dir_okay=False,
496
+ ),
497
+ ArgumentSpec(
498
+ "--force",
499
+ field="force",
500
+ parser_name="force",
501
+ help="Overwrite an existing test report.",
502
+ value_type=ArgumentValueType.BOOLEAN,
503
+ ),
504
+ ArgumentSpec(
505
+ "--yes",
506
+ aliases=("-y",),
507
+ field="yes",
508
+ parser_name="assume_yes",
509
+ help="Skip discovery confirmation.",
510
+ value_type=ArgumentValueType.BOOLEAN,
511
+ ),
512
+ ArgumentSpec(
513
+ "--concurrency",
514
+ aliases=("-j",),
515
+ field="concurrency",
516
+ parser_name="concurrency",
517
+ help="Concurrent analysis jobs.",
518
+ value_type=ArgumentValueType.INTEGER,
519
+ default=4,
520
+ minimum=1,
521
+ maximum=16,
522
+ ),
523
+ ),
524
+ service_options=_SERVICE_OPTIONS,
525
+ )
526
+
527
+ ANALYZER_COPY = CommandSpec(
528
+ path=("analyzer", "copy"),
529
+ help="Copy an analyzer within or across Microsoft Foundry resources.",
530
+ operation="cu_cli_core.operations.analyzer_copy#copy_analyzer",
531
+ request_type="cu_cli_core.contracts#AnalyzerCopyRequest",
532
+ arguments=(
533
+ ArgumentSpec(
534
+ "--source",
535
+ field="source",
536
+ parser_name="named_source",
537
+ help="Analyzer to copy.",
538
+ required=True,
539
+ ),
540
+ ArgumentSpec(
541
+ "SOURCE",
542
+ field="source",
543
+ parser_name="positional_source",
544
+ help="Standalone positional shortcut for --source.",
545
+ classification=SurfaceClassification.STANDALONE_SHORTCUT,
546
+ ),
547
+ ArgumentSpec(
548
+ "--destination",
549
+ field="destination",
550
+ parser_name="named_destination",
551
+ help="New analyzer name to create.",
552
+ required=True,
553
+ ),
554
+ ArgumentSpec(
555
+ "DESTINATION",
556
+ field="destination",
557
+ parser_name="positional_destination",
558
+ help="Standalone positional shortcut for --destination.",
559
+ classification=SurfaceClassification.STANDALONE_SHORTCUT,
560
+ ),
561
+ ArgumentSpec(
562
+ "--source-resource",
563
+ field="source_resource",
564
+ parser_name="source_resource",
565
+ help="Resolve the source resource directly from Azure.",
566
+ ),
567
+ ArgumentSpec(
568
+ "--source-subscription",
569
+ field="source_subscription",
570
+ parser_name="source_subscription",
571
+ help=(
572
+ "Subscription in which to resolve the source resource; "
573
+ "defaults to the active Azure CLI subscription."
574
+ ),
575
+ ),
576
+ ArgumentSpec(
577
+ "--source-resource-group",
578
+ field="source_resource_group",
579
+ parser_name="source_resource_group",
580
+ help="Resource group used for source discovery.",
581
+ ),
582
+ ArgumentSpec(
583
+ "--source-profile",
584
+ field="source_profile",
585
+ parser_name="source_profile",
586
+ help="Standalone named CU CLI profile for the source.",
587
+ classification=SurfaceClassification.STANDALONE_SHORTCUT,
588
+ ),
589
+ ArgumentSpec(
590
+ "--destination-resource",
591
+ field="destination_resource",
592
+ parser_name="destination_resource",
593
+ help="Resolve the destination resource directly from Azure.",
594
+ ),
595
+ ArgumentSpec(
596
+ "--destination-subscription",
597
+ field="destination_subscription",
598
+ parser_name="destination_subscription",
599
+ help=(
600
+ "Subscription in which to resolve the destination resource; "
601
+ "defaults to the active Azure CLI subscription."
602
+ ),
603
+ ),
604
+ ArgumentSpec(
605
+ "--destination-resource-group",
606
+ field="destination_resource_group",
607
+ parser_name="destination_resource_group",
608
+ help="Resource group used for destination discovery.",
609
+ ),
610
+ ArgumentSpec(
611
+ "--destination-profile",
612
+ field="destination_profile",
613
+ parser_name="destination_profile",
614
+ help="Standalone named CU CLI profile for the destination.",
615
+ classification=SurfaceClassification.STANDALONE_SHORTCUT,
616
+ ),
617
+ ),
618
+ service_options=_SERVICE_OPTIONS,
619
+ )
620
+
621
+ ANALYZER_VALIDATE = CommandSpec(
622
+ path=("analyzer", "validate"),
623
+ help="Validate a local analyzer schema.",
624
+ operation="cu_cli_core.operations.validation#validate_schema",
625
+ request_type="cu_cli_core.contracts#AnalyzerValidateRequest",
626
+ arguments=(
627
+ ArgumentSpec(
628
+ "--schema",
629
+ aliases=("-s",),
630
+ field="schema",
631
+ parser_name="named_schema_path",
632
+ help="Path to a JSON analyzer schema.",
633
+ value_type=ArgumentValueType.PATH,
634
+ required=True,
635
+ path_exists=True,
636
+ file_okay=True,
637
+ dir_okay=False,
638
+ ),
639
+ ArgumentSpec(
640
+ "SCHEMA",
641
+ field="schema",
642
+ parser_name="positional_schema_path",
643
+ help="Standalone positional shortcut for --schema.",
644
+ value_type=ArgumentValueType.PATH,
645
+ path_exists=True,
646
+ file_okay=True,
647
+ dir_okay=False,
648
+ classification=SurfaceClassification.STANDALONE_SHORTCUT,
649
+ ),
650
+ ArgumentSpec(
651
+ "--json",
652
+ field="json",
653
+ parser_name="json_output",
654
+ help="Emit structured validation results.",
655
+ value_type=ArgumentValueType.BOOLEAN,
656
+ classification=SurfaceClassification.FRONTEND_PRESENTATION,
657
+ ),
658
+ ArgumentSpec(
659
+ "--strict",
660
+ field="strict",
661
+ parser_name="strict",
662
+ help="Treat warnings as errors.",
663
+ value_type=ArgumentValueType.BOOLEAN,
664
+ ),
665
+ ArgumentSpec(
666
+ "--spec",
667
+ field="spec",
668
+ parser_name="use_spec",
669
+ help="Also validate against the bundled service contract.",
670
+ value_type=ArgumentValueType.BOOLEAN,
671
+ ),
672
+ ),
673
+ service_options=("api-version",),
674
+ )
675
+
676
+ ANALYZER_SCHEMA_CREATE = CommandSpec(
677
+ path=("analyzer", "schema", "create"),
678
+ help="Create a starter schema or derive one from a local document sample.",
679
+ operation="cu_cli_core.operations.schema#create_schema",
680
+ request_type="cu_cli_core.contracts#AnalyzerSchemaCreateRequest",
681
+ arguments=(
682
+ ArgumentSpec(
683
+ "--from-template",
684
+ field="from_template",
685
+ parser_name="from_template",
686
+ help="Create an offline starter schema (the default).",
687
+ value_type=ArgumentValueType.BOOLEAN,
688
+ ),
689
+ ArgumentSpec(
690
+ "--from-sample",
691
+ field="from_sample",
692
+ parser_name="sample_path",
693
+ help="Create an extraction schema from one local document sample.",
694
+ value_type=ArgumentValueType.PATH,
695
+ path_exists=True,
696
+ file_okay=True,
697
+ dir_okay=False,
698
+ ),
699
+ ArgumentSpec(
700
+ "--name",
701
+ aliases=("-n", "-a"),
702
+ field="name",
703
+ parser_name="analyzer_id",
704
+ help="Custom analyzer name.",
705
+ default="my_analyzer_v1",
706
+ ),
707
+ ArgumentSpec(
708
+ "--base",
709
+ field="base",
710
+ parser_name="base",
711
+ help="Base analyzer name; defaults from --modality.",
712
+ ),
713
+ ArgumentSpec(
714
+ "--modality",
715
+ field="modality",
716
+ parser_name="modality",
717
+ help="Modality used to choose a base analyzer.",
718
+ default="document",
719
+ choices=("document", "image", "audio", "video"),
720
+ ),
721
+ ArgumentSpec(
722
+ "--output-file",
723
+ field="output_file",
724
+ parser_name="out_path",
725
+ help="Write the schema to a file.",
726
+ value_type=ArgumentValueType.PATH,
727
+ file_okay=True,
728
+ dir_okay=False,
729
+ ),
730
+ ArgumentSpec(
731
+ "--force",
732
+ field="force",
733
+ parser_name="force",
734
+ help="Overwrite an existing schema output file.",
735
+ value_type=ArgumentValueType.BOOLEAN,
736
+ ),
737
+ ArgumentSpec(
738
+ "--type",
739
+ field="template_type",
740
+ parser_name="template_type",
741
+ help="Schema template style.",
742
+ default="extraction",
743
+ choices=("extraction", "classification"),
744
+ ),
745
+ ),
746
+ service_options=_SERVICE_OPTIONS,
747
+ )
748
+
749
+ DEFAULTS_SHOW = CommandSpec(
750
+ path=("defaults", "show"),
751
+ help="Show Content Understanding defaults that map models to deployments.",
752
+ operation="cu_cli_core.defaults#get_defaults",
753
+ request_type="cu_cli_core.contracts#DefaultsShowRequest",
754
+ arguments=(
755
+ ArgumentSpec(
756
+ "--table",
757
+ field="table",
758
+ parser_name="table_output",
759
+ help="Print model mappings as a readable table.",
760
+ value_type=ArgumentValueType.BOOLEAN,
761
+ classification=SurfaceClassification.FRONTEND_PRESENTATION,
762
+ ),
763
+ ),
764
+ service_options=_SERVICE_OPTIONS,
765
+ )
766
+
767
+ DEFAULTS_SET = CommandSpec(
768
+ path=("defaults", "set"),
769
+ help="Configure Content Understanding defaults that map models to deployments.",
770
+ operation="cu_cli_core.defaults#apply_defaults",
771
+ request_type="cu_cli_core.contracts#DefaultsSetRequest",
772
+ arguments=(
773
+ ArgumentSpec(
774
+ "--model",
775
+ field="models",
776
+ parser_name="model_kv",
777
+ help="Model deployment mapping in MODEL=DEPLOYMENT form.",
778
+ repeatable=True,
779
+ ),
780
+ ArgumentSpec(
781
+ "--from-profile",
782
+ field="from_profile",
783
+ parser_name="from_profile",
784
+ help="Include mappings from the effective standalone CU CLI profile.",
785
+ value_type=ArgumentValueType.BOOLEAN,
786
+ default=False,
787
+ classification=SurfaceClassification.STANDALONE_SHORTCUT,
788
+ ),
789
+ ArgumentSpec(
790
+ "--replace",
791
+ field="replace",
792
+ parser_name="replace",
793
+ help="Replace defaults instead of merging.",
794
+ value_type=ArgumentValueType.BOOLEAN,
795
+ ),
796
+ ArgumentSpec(
797
+ "--json",
798
+ field="json",
799
+ parser_name="json_output",
800
+ help="Emit the complete updated defaults object.",
801
+ value_type=ArgumentValueType.BOOLEAN,
802
+ classification=SurfaceClassification.FRONTEND_PRESENTATION,
803
+ ),
804
+ ),
805
+ service_options=_SERVICE_OPTIONS,
806
+ )
807
+
808
+ PROFILE_SHOW = CommandSpec(
809
+ path=("profile", "show"),
810
+ help="Show the effective CU CLI profile with secrets redacted.",
811
+ operation="cu_cli_core.operations.profiles#show_profile",
812
+ request_type="cu_cli_core.contracts#ProfileShowRequest",
813
+ arguments=(
814
+ ArgumentSpec(
815
+ "--name",
816
+ aliases=("-n",),
817
+ field="name",
818
+ parser_name="profile_name",
819
+ help="CU CLI profile to show; defaults to the active CU CLI profile.",
820
+ ),
821
+ ArgumentSpec(
822
+ "--deployments",
823
+ field="deployments",
824
+ parser_name="deployments",
825
+ help="Also list live Foundry model deployments.",
826
+ value_type=ArgumentValueType.BOOLEAN,
827
+ classification=SurfaceClassification.FRONTEND_PRESENTATION,
828
+ ),
829
+ ),
830
+ )
831
+
832
+ PROFILE_LIST = CommandSpec(
833
+ path=("profile", "list"),
834
+ help="List saved CU CLI profiles and identify the active CU CLI profile.",
835
+ operation="cu_cli_core.operations.profiles#list_profiles",
836
+ request_type="cu_cli_core.contracts#ProfileListRequest",
837
+ )
838
+
839
+ PROFILE_GET = CommandSpec(
840
+ path=("profile", "get"),
841
+ help="Print one saved CU CLI profile value.",
842
+ operation="cu_cli_core.operations.profiles#get_profile_value",
843
+ request_type="cu_cli_core.contracts#ProfileGetRequest",
844
+ arguments=(
845
+ ArgumentSpec(
846
+ "--key",
847
+ field="key",
848
+ parser_name="profile_key",
849
+ help="CU CLI profile setting key.",
850
+ required=True,
851
+ ),
852
+ ArgumentSpec(
853
+ "KEY",
854
+ field="key",
855
+ parser_name="positional_profile_key",
856
+ help="Standalone positional shortcut for --key.",
857
+ classification=SurfaceClassification.STANDALONE_SHORTCUT,
858
+ ),
859
+ ArgumentSpec(
860
+ "--name",
861
+ aliases=("-n",),
862
+ field="name",
863
+ parser_name="profile_name",
864
+ help="CU CLI profile to inspect; defaults to the active CU CLI profile.",
865
+ ),
866
+ ),
867
+ )
868
+
869
+ PROFILE_SET = CommandSpec(
870
+ path=("profile", "set"),
871
+ help="Set one saved CU CLI profile value.",
872
+ operation="cu_cli_core.operations.profiles#set_profile_value",
873
+ request_type="cu_cli_core.contracts#ProfileSetRequest",
874
+ arguments=(
875
+ ArgumentSpec(
876
+ "--key",
877
+ field="key",
878
+ parser_name="profile_key",
879
+ help="CU CLI profile setting key.",
880
+ required=True,
881
+ ),
882
+ ArgumentSpec(
883
+ "KEY",
884
+ field="key",
885
+ parser_name="positional_profile_key",
886
+ help="Standalone positional shortcut for --key.",
887
+ classification=SurfaceClassification.STANDALONE_SHORTCUT,
888
+ ),
889
+ ArgumentSpec(
890
+ "--value",
891
+ field="value",
892
+ parser_name="profile_value",
893
+ help="Value to save.",
894
+ required=True,
895
+ ),
896
+ ArgumentSpec(
897
+ "VALUE",
898
+ field="value",
899
+ parser_name="positional_profile_value",
900
+ help="Standalone positional shortcut for --value.",
901
+ classification=SurfaceClassification.STANDALONE_SHORTCUT,
902
+ ),
903
+ ArgumentSpec(
904
+ "--name",
905
+ aliases=("-n",),
906
+ field="name",
907
+ parser_name="profile_name",
908
+ help="CU CLI profile to update; defaults to the active CU CLI profile.",
909
+ ),
910
+ ),
911
+ )
912
+
913
+ PROFILE_UNSET = CommandSpec(
914
+ path=("profile", "unset"),
915
+ help="Remove one explicitly saved CU CLI profile value.",
916
+ operation="cu_cli_core.operations.profiles#unset_profile_value",
917
+ request_type="cu_cli_core.contracts#ProfileUnsetRequest",
918
+ arguments=PROFILE_GET.arguments,
919
+ )
920
+
921
+ PROFILE_CREATE = CommandSpec(
922
+ path=("profile", "create"),
923
+ help="Create an empty named CU CLI profile without activating it.",
924
+ operation="cu_cli_core.operations.profiles#create_profile",
925
+ request_type="cu_cli_core.contracts#ProfileCreateRequest",
926
+ arguments=_profile_name_arguments(
927
+ "New profile name. Use 1-64 ASCII letters or numbers, with "
928
+ "hyphens (-) or underscores (_); 'default' and "
929
+ "'model_deployments' are reserved."
930
+ ),
931
+ )
932
+
933
+ PROFILE_DELETE = CommandSpec(
934
+ path=("profile", "delete"),
935
+ help="Delete an inactive named CU CLI profile.",
936
+ operation="cu_cli_core.operations.profiles#delete_profile",
937
+ request_type="cu_cli_core.contracts#ProfileDeleteRequest",
938
+ arguments=_profile_name_arguments("Existing inactive profile to delete."),
939
+ )
940
+
941
+ PROFILE_COPY = CommandSpec(
942
+ path=("profile", "copy"),
943
+ help="Copy a CU CLI profile to a new name.",
944
+ operation="cu_cli_core.operations.profiles#copy_profile",
945
+ request_type="cu_cli_core.contracts#ProfileCopyRequest",
946
+ arguments=(
947
+ ArgumentSpec(
948
+ "--source",
949
+ field="source",
950
+ parser_name="source_profile",
951
+ help="Source CU CLI profile; defaults to the active CU CLI profile.",
952
+ ),
953
+ ArgumentSpec(
954
+ "--destination",
955
+ field="destination",
956
+ parser_name="destination_profile",
957
+ help=(
958
+ "New destination profile name. Use 1-64 ASCII letters or numbers, "
959
+ "with hyphens (-) or underscores (_); 'default' and "
960
+ "'model_deployments' are reserved."
961
+ ),
962
+ required=True,
963
+ ),
964
+ ArgumentSpec(
965
+ "SOURCE",
966
+ field="source",
967
+ parser_name="positional_source_profile",
968
+ help="Standalone source-profile shortcut.",
969
+ classification=SurfaceClassification.STANDALONE_SHORTCUT,
970
+ ),
971
+ ArgumentSpec(
972
+ "DESTINATION",
973
+ field="destination",
974
+ parser_name="positional_destination_profile",
975
+ help="Standalone destination-profile shortcut.",
976
+ classification=SurfaceClassification.STANDALONE_SHORTCUT,
977
+ ),
978
+ ),
979
+ )
980
+
981
+ PROFILE_RENAME = CommandSpec(
982
+ path=("profile", "rename"),
983
+ help="Rename a CU CLI profile while preserving its values and active state.",
984
+ operation="cu_cli_core.operations.profiles#rename_profile",
985
+ request_type="cu_cli_core.contracts#ProfileRenameRequest",
986
+ arguments=(
987
+ ArgumentSpec(
988
+ "--source",
989
+ field="source",
990
+ parser_name="source_profile",
991
+ help="Existing profile name.",
992
+ required=True,
993
+ ),
994
+ ArgumentSpec(
995
+ "--destination",
996
+ field="destination",
997
+ parser_name="destination_profile",
998
+ help=(
999
+ "New profile name. Use 1-64 ASCII letters or numbers, with "
1000
+ "hyphens (-) or underscores (_); 'default' and "
1001
+ "'model_deployments' are reserved."
1002
+ ),
1003
+ required=True,
1004
+ ),
1005
+ ArgumentSpec(
1006
+ "SOURCE",
1007
+ field="source",
1008
+ parser_name="positional_source_profile",
1009
+ help="Standalone source-profile shortcut.",
1010
+ classification=SurfaceClassification.STANDALONE_SHORTCUT,
1011
+ ),
1012
+ ArgumentSpec(
1013
+ "DESTINATION",
1014
+ field="destination",
1015
+ parser_name="positional_destination_profile",
1016
+ help="Standalone destination-profile shortcut.",
1017
+ classification=SurfaceClassification.STANDALONE_SHORTCUT,
1018
+ ),
1019
+ ),
1020
+ )
1021
+
1022
+ PROFILE_SET_ACTIVE = CommandSpec(
1023
+ path=("profile", "set-active"),
1024
+ help="Select the active CU CLI profile.",
1025
+ operation="cu_cli_core.operations.profiles#set_active_profile",
1026
+ request_type="cu_cli_core.contracts#ProfileSetActiveRequest",
1027
+ arguments=_profile_name_arguments("Existing profile to activate."),
1028
+ )
1029
+
1030
+ PROFILE_SYNC_DEFAULTS = CommandSpec(
1031
+ path=("profile", "sync-defaults"),
1032
+ help="Refresh a profile's model mappings from Content Understanding defaults.",
1033
+ operation="cu_cli_core.operations.profiles#sync_profile_models",
1034
+ request_type="cu_cli_core.contracts#ProfileSyncModelsRequest",
1035
+ arguments=(
1036
+ ArgumentSpec(
1037
+ "--name",
1038
+ aliases=("-n",),
1039
+ field="name",
1040
+ parser_name="profile_name",
1041
+ help="CU CLI profile to synchronize; defaults to the active CU CLI profile.",
1042
+ ),
1043
+ ),
1044
+ service_options=("auth-mode", "api-key"),
1045
+ )
1046
+
1047
+ ENV_VAR_LIST = CommandSpec(
1048
+ path=("env-var", "list"),
1049
+ help="List recognized environment variables that are currently set.",
1050
+ operation="cu_cli_core.environment#list_set_environment_variables",
1051
+ request_type="cu_cli_core.contracts#EnvironmentVariableListRequest",
1052
+ arguments=(
1053
+ ArgumentSpec(
1054
+ "--json",
1055
+ field="json",
1056
+ parser_name="json_output",
1057
+ help="Print set variables as redacted JSON.",
1058
+ value_type=ArgumentValueType.BOOLEAN,
1059
+ classification=SurfaceClassification.FRONTEND_PRESENTATION,
1060
+ ),
1061
+ ),
1062
+ )
1063
+
1064
+
1065
+ COMMAND_SPECS: tuple[CommandSpec, ...] = (
1066
+ ANALYZE,
1067
+ ANALYZER_LIST,
1068
+ ANALYZER_SHOW,
1069
+ ANALYZER_CREATE,
1070
+ ANALYZER_COPY,
1071
+ ANALYZER_DELETE,
1072
+ ANALYZER_VALIDATE,
1073
+ ANALYZER_TEST,
1074
+ ANALYZER_SCHEMA_CREATE,
1075
+ DEFAULTS_SHOW,
1076
+ DEFAULTS_SET,
1077
+ PROFILE_SHOW,
1078
+ PROFILE_LIST,
1079
+ PROFILE_GET,
1080
+ PROFILE_SET,
1081
+ PROFILE_UNSET,
1082
+ PROFILE_CREATE,
1083
+ PROFILE_DELETE,
1084
+ PROFILE_COPY,
1085
+ PROFILE_RENAME,
1086
+ PROFILE_SET_ACTIVE,
1087
+ PROFILE_SYNC_DEFAULTS,
1088
+ ENV_VAR_LIST,
1089
+ )
1090
+ _COMMAND_SPECS_BY_PATH = {spec.path: spec for spec in COMMAND_SPECS}
1091
+
1092
+ if len(_COMMAND_SPECS_BY_PATH) != len(COMMAND_SPECS):
1093
+ raise RuntimeError("duplicate command path in COMMAND_SPECS")
1094
+
1095
+
1096
+ def get_command_spec(*path: str) -> CommandSpec:
1097
+ """Return the static specification for ``path``."""
1098
+
1099
+ return _COMMAND_SPECS_BY_PATH[tuple(path)]