systemlink-cli 1.20.0__tar.gz → 1.22.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (155) hide show
  1. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/PKG-INFO +1 -1
  2. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/pyproject.toml +1 -1
  3. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/_version.py +1 -1
  4. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/example_click.py +108 -17
  5. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/example_loader.py +36 -2
  6. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/example_provisioner.py +1228 -175
  7. systemlink_cli-1.22.0/slcli/examples/README.md +205 -0
  8. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/examples/_schema/schema-v1.0.json +44 -4
  9. {systemlink_cli-1.20.0/slcli/examples/demo-complete-workflow → systemlink_cli-1.22.0/slcli/examples/demo-data-1}/README.md +9 -9
  10. {systemlink_cli-1.20.0/slcli/examples/demo-complete-workflow → systemlink_cli-1.22.0/slcli/examples/demo-data-1}/config.yaml +27 -27
  11. {systemlink_cli-1.20.0/slcli/examples/demo-test-plans → systemlink_cli-1.22.0/slcli/examples/demo-data-2}/README.md +7 -7
  12. {systemlink_cli-1.20.0/slcli/examples/demo-test-plans → systemlink_cli-1.22.0/slcli/examples/demo-data-2}/config.yaml +1 -1
  13. systemlink_cli-1.22.0/slcli/examples/demo-data-3/config.yaml +870 -0
  14. systemlink_cli-1.22.0/slcli/examples/demo-data-3/leakage-current-results.json +28 -0
  15. systemlink_cli-1.22.0/slcli/examples/demo-data-3/overvoltage-results.json +29 -0
  16. systemlink_cli-1.22.0/slcli/examples/demo-data-3/product-xyz-compliance-report.json +46 -0
  17. systemlink_cli-1.22.0/slcli/examples/demo-data-3/product-xyz-specification.csv +6 -0
  18. systemlink_cli-1.22.0/slcli/examples/demo-data-3/thermal-response-results.json +27 -0
  19. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/skills/slcli/SKILL.md +9 -4
  20. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/skills/slcli/references/commands.md +7 -0
  21. systemlink_cli-1.22.0/slcli/skills/slcli/references/example-authoring.md +193 -0
  22. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/utils.py +1 -1
  23. systemlink_cli-1.20.0/slcli/examples/README.md +0 -134
  24. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/LICENSE +0 -0
  25. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/dff-editor/editor.js +0 -0
  26. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/dff-editor/index.html +0 -0
  27. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/__init__.py +0 -0
  28. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/__main__.py +0 -0
  29. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/alarm_click.py +0 -0
  30. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/asset_click.py +0 -0
  31. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/cli_formatters.py +0 -0
  32. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/cli_utils.py +0 -0
  33. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/comment_click.py +0 -0
  34. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/completion_click.py +0 -0
  35. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/config.py +0 -0
  36. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/config_click.py +0 -0
  37. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/dataframe_click.py +0 -0
  38. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/dff_click.py +0 -0
  39. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/dff_decorators.py +0 -0
  40. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/examples/exercise-5-1-parametric-insights/README.md +0 -0
  41. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/examples/exercise-5-1-parametric-insights/config.yaml +0 -0
  42. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/examples/exercise-7-1-test-plans/README.md +0 -0
  43. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/examples/exercise-7-1-test-plans/config.yaml +0 -0
  44. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/examples/spec-compliance-notebooks/README.md +0 -0
  45. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/examples/spec-compliance-notebooks/config.yaml +0 -0
  46. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/examples/spec-compliance-notebooks/notebooks/SpecAnalysis_ComplianceCalculation.ipynb +0 -0
  47. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/examples/spec-compliance-notebooks/notebooks/SpecComplianceCalculation.ipynb +0 -0
  48. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/examples/spec-compliance-notebooks/notebooks/SpecfileExtractionAndIngestion.ipynb +0 -0
  49. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/examples/spec-compliance-notebooks/spec_template.xlsx +0 -0
  50. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/feed_click.py +0 -0
  51. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/file_click.py +0 -0
  52. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/function_click.py +0 -0
  53. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/function_templates.py +0 -0
  54. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/main.py +0 -0
  55. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/mcp_click.py +0 -0
  56. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/mcp_reachability.py +0 -0
  57. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/mcp_server.py +0 -0
  58. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/notebook_click.py +0 -0
  59. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/platform.py +0 -0
  60. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/policy_click.py +0 -0
  61. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/policy_utils.py +0 -0
  62. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/profiles.py +0 -0
  63. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/response_handlers.py +0 -0
  64. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/rich_output.py +0 -0
  65. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/routine_click.py +0 -0
  66. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/skill_click.py +0 -0
  67. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/skills/slcli/references/analysis-recipes.md +0 -0
  68. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/skills/slcli/references/datasheet-workflow.md +0 -0
  69. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/skills/slcli/references/filtering.md +0 -0
  70. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/skills/slcli/references/import-specs.min.json +0 -0
  71. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/skills/slcli/references/job-debugging/overview.md +0 -0
  72. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/skills/slcli/references/nipkg/overview.md +0 -0
  73. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/skills/slcli/references/notebook/interfaces.md +0 -0
  74. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/skills/slcli/references/notebook/notebook-patterns.md +0 -0
  75. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/skills/slcli/references/notebook/overview.md +0 -0
  76. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/skills/slcli/references/python-test/overview.md +0 -0
  77. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/skills/slcli/references/routine-examples.md +0 -0
  78. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/skills/slcli/references/troubleshooting.md +0 -0
  79. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/skills/slcli/references/webapp/angular-ui-packages.md +0 -0
  80. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/skills/slcli/references/webapp/deployment.md +0 -0
  81. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/skills/slcli/references/webapp/layout-patterns.md +0 -0
  82. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/skills/slcli/references/webapp/nimble-angular.md +0 -0
  83. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/skills/slcli/references/webapp/overview.md +0 -0
  84. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/skills/slcli/references/webapp/systemlink-services.md +0 -0
  85. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/skills/slcli/references/webapp/troubleshooting.md +0 -0
  86. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/skills/slcli/scripts/spec_import_helper.py +0 -0
  87. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/spec_click.py +0 -0
  88. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/ssl_trust.py +0 -0
  89. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/state_click.py +0 -0
  90. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/system_click.py +0 -0
  91. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/system_query_utils.py +0 -0
  92. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/table_utils.py +0 -0
  93. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/tag_click.py +0 -0
  94. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/templates_click.py +0 -0
  95. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/testmonitor_click.py +0 -0
  96. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/universal_handlers.py +0 -0
  97. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/user_click.py +0 -0
  98. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/web_editor.py +0 -0
  99. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/webapp_bootstrap.py +0 -0
  100. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/webapp_click.py +0 -0
  101. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/.gitignore +0 -0
  102. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/README.md +0 -0
  103. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/angular.json +0 -0
  104. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/package.json +0 -0
  105. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/app/app-routing.module.ts +0 -0
  106. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/app/app.component.html +0 -0
  107. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/app/app.component.scss +0 -0
  108. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/app/app.component.ts +0 -0
  109. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/app/app.module.ts +0 -0
  110. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/app/core/layout/app-shell.component.html +0 -0
  111. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/app/core/layout/app-shell.component.scss +0 -0
  112. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/app/core/layout/app-shell.component.ts +0 -0
  113. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/app/core/state/app-view-state.service.ts +0 -0
  114. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/app/core/systemlink/systemlink-context.service.ts +0 -0
  115. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/app/core/systemlink/theme-sync.service.ts +0 -0
  116. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/app/core/systemlink/webapp-home-data.service.ts +0 -0
  117. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/app/features/assets/assets-page.component.html +0 -0
  118. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/app/features/assets/assets-page.component.scss +0 -0
  119. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/app/features/assets/assets-page.component.ts +0 -0
  120. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/app/features/datasets/datasets-page.component.html +0 -0
  121. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/app/features/datasets/datasets-page.component.scss +0 -0
  122. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/app/features/datasets/datasets-page.component.ts +0 -0
  123. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/app/features/home/home-page.component.html +0 -0
  124. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/app/features/home/home-page.component.scss +0 -0
  125. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/app/features/home/home-page.component.ts +0 -0
  126. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/app/features/master-detail/master-detail-page.component.html +0 -0
  127. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/app/features/master-detail/master-detail-page.component.scss +0 -0
  128. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/app/features/master-detail/master-detail-page.component.ts +0 -0
  129. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/app/features/operations/operations-page.component.html +0 -0
  130. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/app/features/operations/operations-page.component.scss +0 -0
  131. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/app/features/operations/operations-page.component.ts +0 -0
  132. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/app/features/settings/settings-page.component.html +0 -0
  133. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/app/features/settings/settings-page.component.scss +0 -0
  134. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/app/features/settings/settings-page.component.ts +0 -0
  135. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/app/shared/components/empty-state.component.html +0 -0
  136. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/app/shared/components/empty-state.component.scss +0 -0
  137. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/app/shared/components/empty-state.component.ts +0 -0
  138. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/app/shared/components/error-banner.component.html +0 -0
  139. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/app/shared/components/error-banner.component.scss +0 -0
  140. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/app/shared/components/error-banner.component.ts +0 -0
  141. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/app/shared/components/loading-state.component.html +0 -0
  142. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/app/shared/components/loading-state.component.scss +0 -0
  143. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/app/shared/components/loading-state.component.ts +0 -0
  144. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/app/shared/states/view-state.model.ts +0 -0
  145. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/assets/README.md +0 -0
  146. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/index.html +0 -0
  147. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/main.ts +0 -0
  148. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/src/styles.scss +0 -0
  149. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/tsconfig.app.json +0 -0
  150. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/webapp_templates/angular/blank/tsconfig.json +0 -0
  151. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/workflow_preview.py +0 -0
  152. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/workflows_click.py +0 -0
  153. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/workitem_click.py +0 -0
  154. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/workspace_click.py +0 -0
  155. {systemlink_cli-1.20.0 → systemlink_cli-1.22.0}/slcli/workspace_utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: systemlink-cli
3
- Version: 1.20.0
3
+ Version: 1.22.0
4
4
  Summary: SystemLink Integrator CLI - cross-platform CLI for SystemLink workflows and templates.
5
5
  License-File: LICENSE
6
6
  Author: Fred Visser
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "systemlink-cli"
3
- version = "1.20.0"
3
+ version = "1.22.0"
4
4
  description = "SystemLink Integrator CLI - cross-platform CLI for SystemLink workflows and templates."
5
5
  authors = ["Fred Visser <fred.visser@emerson.com>"]
6
6
  packages = [{ include = "slcli" }]
@@ -1,4 +1,4 @@
1
1
  """Version information for slcli."""
2
2
 
3
3
  # This file is auto-generated. Do not edit manually.
4
- __version__ = "1.20.0"
4
+ __version__ = "1.22.0"
@@ -2,6 +2,7 @@
2
2
 
3
3
  import json
4
4
  import sys
5
+ from pathlib import Path
5
6
  from typing import Any, Dict, List, Optional
6
7
 
7
8
  import click
@@ -57,11 +58,60 @@ def _serialize_results(results: List[ProvisioningResult]) -> List[Dict[str, Any]
57
58
  "action": action_value,
58
59
  "server_id": res.server_id,
59
60
  "error": res.error,
61
+ "details": res.details,
60
62
  }
61
63
  )
62
64
  return serialized
63
65
 
64
66
 
67
+ def _build_install_manifest(
68
+ config: Dict[str, Any],
69
+ workspace_id: Optional[str],
70
+ results: List[Dict[str, Any]],
71
+ logical_ids: Dict[str, str],
72
+ ) -> Dict[str, Any]:
73
+ """Build the opt-in manifest contract for data-heavy fixtures."""
74
+ resource_actions: Dict[str, List[Dict[str, Any]]] = {
75
+ "created": [],
76
+ "updated": [],
77
+ "skipped": [],
78
+ "failed": [],
79
+ }
80
+ for result in results:
81
+ action = result.get("action")
82
+ if action in resource_actions:
83
+ resource_actions[action].append(result)
84
+
85
+ validation = config.get("validation", {})
86
+ if not isinstance(validation, dict):
87
+ validation = {}
88
+ unsupported = validation.get("unsupported", [])
89
+ if not isinstance(unsupported, list):
90
+ unsupported = [str(unsupported)]
91
+ required_relationships = validation.get("required_relationships", [])
92
+ if not isinstance(required_relationships, list):
93
+ required_relationships = [str(required_relationships)]
94
+
95
+ failed_relationships = validation.get("failed_relationships", [])
96
+ if not isinstance(failed_relationships, list):
97
+ failed_relationships = [str(failed_relationships)]
98
+
99
+ incomplete = bool(resource_actions["failed"] or unsupported or failed_relationships)
100
+ return {
101
+ "example": config.get("name", ""),
102
+ "version": config.get("example_version", "1.0.0"),
103
+ "workspace": {"name": config.get("workspace_name", ""), "id": workspace_id},
104
+ "resources": resource_actions,
105
+ "logical_ids": logical_ids,
106
+ "validation": {
107
+ "required_relationships": required_relationships,
108
+ "failed_relationships": failed_relationships,
109
+ "unsupported": unsupported,
110
+ "complete": not incomplete,
111
+ },
112
+ }
113
+
114
+
65
115
  def _result_row_formatter(item: Dict[str, Any]) -> List[str]:
66
116
  """Formatter for table rows in provisioning output."""
67
117
  return [
@@ -101,9 +151,7 @@ def _output_results(results: List[Dict[str, Any]], format_output: str) -> None:
101
151
  )
102
152
 
103
153
 
104
- def _write_audit_log(
105
- results: List[Dict[str, Any]], audit_log: Optional[str], quiet: bool = False
106
- ) -> None:
154
+ def _write_audit_log(results: Any, audit_log: Optional[str], quiet: bool = False) -> None:
107
155
  """Persist results to an audit log file if requested."""
108
156
  if not audit_log:
109
157
  return
@@ -189,7 +237,7 @@ def register_example_commands(cli: Any) -> None:
189
237
  estimated setup time.
190
238
 
191
239
  Example:
192
- slcli example info demo-test-plans
240
+ slcli example info demo-data-2
193
241
  """
194
242
  try:
195
243
  loader = ExampleLoader()
@@ -234,7 +282,13 @@ def register_example_commands(cli: Any) -> None:
234
282
  handle_api_error(exc)
235
283
 
236
284
  @example.command(name="install")
237
- @click.argument("example_name")
285
+ @click.argument("example_name", required=False)
286
+ @click.option(
287
+ "--file",
288
+ "config_file",
289
+ type=click.Path(exists=True, dir_okay=False, readable=True, resolve_path=True),
290
+ help="Path to an example config.yaml; referenced files are relative to its directory.",
291
+ )
238
292
  @click.option("--workspace", "-w", required=True, help="Workspace name or ID for resources")
239
293
  @click.option(
240
294
  "--format",
@@ -255,7 +309,8 @@ def register_example_commands(cli: Any) -> None:
255
309
  help="Path to write provisioning results as JSON for auditing.",
256
310
  )
257
311
  def install_example(
258
- example_name: str,
312
+ example_name: Optional[str],
313
+ config_file: Optional[str],
259
314
  workspace: Optional[str],
260
315
  format: str,
261
316
  dry_run: bool,
@@ -264,26 +319,62 @@ def register_example_commands(cli: Any) -> None:
264
319
  """Provision all resources defined by an example configuration."""
265
320
  try:
266
321
  loader = ExampleLoader()
267
- config = loader.load_config(example_name)
322
+ example_dir: Optional[Path] = None
323
+ if config_file and example_name:
324
+ raise ValueError("Specify either EXAMPLE_NAME or --file, not both.")
325
+ if not config_file and not example_name:
326
+ raise ValueError("Provide an EXAMPLE_NAME or --file path to config.yaml.")
327
+
328
+ if config_file:
329
+ config_path = Path(config_file)
330
+ config = loader.load_config_file(config_path)
331
+ example_name = str(config["name"])
332
+ example_dir = config_path.parent
333
+ else:
334
+ config = loader.load_config(example_name or "")
268
335
 
269
336
  workspace_id = _resolve_workspace_id(get_effective_workspace(workspace))
270
- provisioner = ExampleProvisioner(
271
- workspace_id=workspace_id,
272
- example_name=example_name,
273
- dry_run=dry_run,
274
- )
337
+ provisioner_options: Dict[str, Any] = {
338
+ "workspace_id": workspace_id,
339
+ "example_name": example_name,
340
+ "dry_run": dry_run,
341
+ }
342
+ if example_dir is not None:
343
+ provisioner_options["example_dir"] = example_dir
344
+ provisioner = ExampleProvisioner(**provisioner_options)
275
345
 
276
346
  results, err = provisioner.provision(config)
277
347
  if err:
278
348
  handle_api_error(err)
279
349
 
280
350
  serialized = _serialize_results(results)
281
- _write_audit_log(serialized, audit_log, quiet=format == "json")
282
- _output_results(serialized, format)
283
-
351
+ manifest_mode = bool(config.get("install_manifest"))
352
+ manifest = _build_install_manifest(
353
+ config,
354
+ workspace_id,
355
+ serialized,
356
+ getattr(provisioner, "id_map", {}),
357
+ )
284
358
  failed = any(r.get("action") == ProvisioningAction.FAILED.value for r in serialized)
285
- if failed:
286
- click.echo("✗ One or more resources failed to provision.", err=True)
359
+ incomplete = manifest["validation"]["complete"] is False
360
+
361
+ if manifest_mode and format == "json":
362
+ _write_audit_log(manifest, audit_log, quiet=True)
363
+ click.echo(json.dumps(manifest, indent=2))
364
+ else:
365
+ _write_audit_log(serialized, audit_log, quiet=format == "json")
366
+ _output_results(serialized, format)
367
+
368
+ if failed or (manifest_mode and incomplete):
369
+ if manifest_mode and incomplete and manifest["validation"]["unsupported"]:
370
+ click.echo(
371
+ "✗ Example install is incomplete: required capabilities are unsupported.",
372
+ err=True,
373
+ )
374
+ elif failed:
375
+ click.echo("✗ One or more resources failed to provision.", err=True)
376
+ else:
377
+ click.echo("✗ Example install is incomplete.", err=True)
287
378
  sys.exit(ExitCodes.GENERAL_ERROR)
288
379
 
289
380
  if format == "json":
@@ -33,6 +33,11 @@ class ExampleLoader:
33
33
  "data_table",
34
34
  "file",
35
35
  "notebook",
36
+ "state",
37
+ "tag",
38
+ "specification",
39
+ "feed",
40
+ "alarm",
36
41
  }
37
42
 
38
43
  def __init__(self, examples_dir: Optional[Path] = None) -> None:
@@ -100,6 +105,31 @@ class ExampleLoader:
100
105
  f"Example '{example_name}' not found. " f"Config path: {config_path}"
101
106
  )
102
107
 
108
+ return self._load_config(config_path, example_name)
109
+
110
+ def load_config_file(self, config_file: Path) -> Dict[str, Any]:
111
+ """Load and validate an example configuration from an arbitrary file.
112
+
113
+ Args:
114
+ config_file: Path to the example's YAML configuration file.
115
+
116
+ Returns:
117
+ Validated config dictionary.
118
+
119
+ Raises:
120
+ FileNotFoundError: If the config file does not exist.
121
+ ValueError: If config fails validation.
122
+ """
123
+ config_path = config_file.expanduser().resolve()
124
+ if not config_path.exists():
125
+ raise FileNotFoundError(f"Example config not found: {config_path}")
126
+ if not config_path.is_file():
127
+ raise ValueError(f"Example config is not a file: {config_path}")
128
+
129
+ return self._load_config(config_path, str(config_path))
130
+
131
+ def _load_config(self, config_path: Path, source_name: str) -> Dict[str, Any]:
132
+ """Load and validate a config at a known path."""
103
133
  # Load YAML
104
134
  try:
105
135
  with open(config_path, "r") as f:
@@ -113,14 +143,14 @@ class ExampleLoader:
113
143
  # Validate schema
114
144
  errors = self.validate_config(config)
115
145
  if errors:
116
- msg = f"Config validation failed for '{example_name}':\n"
146
+ msg = f"Config validation failed for '{source_name}':\n"
117
147
  msg += "\n".join(f" - {e}" for e in errors)
118
148
  raise ValueError(msg)
119
149
 
120
150
  # Validate references
121
151
  ref_errors = self._validate_references(config)
122
152
  if ref_errors:
123
- msg = f"Reference validation failed for '{example_name}':\n"
153
+ msg = f"Reference validation failed for '{source_name}':\n"
124
154
  msg += "\n".join(f" - {e}" for e in ref_errors)
125
155
  raise ValueError(msg)
126
156
 
@@ -143,6 +173,10 @@ class ExampleLoader:
143
173
  if missing_fields:
144
174
  errors.append(f"Missing required fields: {', '.join(sorted(missing_fields))}")
145
175
 
176
+ name = config.get("name")
177
+ if "name" in config and (not isinstance(name, str) or not name.strip()):
178
+ errors.append("name must be a non-empty string")
179
+
146
180
  # Check format_version is supported
147
181
  version = config.get("format_version")
148
182
  if version and version not in self.SUPPORTED_SCHEMA_VERSIONS: