systemlink-cli 1.22.0__tar.gz → 1.23.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 (154) hide show
  1. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/PKG-INFO +1 -1
  2. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/pyproject.toml +1 -1
  3. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/_version.py +1 -1
  4. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/example_loader.py +1 -0
  5. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/example_provisioner.py +374 -5
  6. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/examples/README.md +48 -5
  7. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/examples/_schema/schema-v1.0.json +2 -1
  8. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/examples/demo-data-3/config.yaml +39 -5
  9. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/skills/slcli/SKILL.md +6 -0
  10. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/skills/slcli/references/example-authoring.md +57 -0
  11. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/webapp_bootstrap.py +35 -1
  12. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/webapp_click.py +5 -1
  13. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/LICENSE +0 -0
  14. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/dff-editor/editor.js +0 -0
  15. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/dff-editor/index.html +0 -0
  16. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/__init__.py +0 -0
  17. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/__main__.py +0 -0
  18. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/alarm_click.py +0 -0
  19. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/asset_click.py +0 -0
  20. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/cli_formatters.py +0 -0
  21. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/cli_utils.py +0 -0
  22. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/comment_click.py +0 -0
  23. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/completion_click.py +0 -0
  24. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/config.py +0 -0
  25. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/config_click.py +0 -0
  26. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/dataframe_click.py +0 -0
  27. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/dff_click.py +0 -0
  28. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/dff_decorators.py +0 -0
  29. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/example_click.py +0 -0
  30. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/examples/demo-data-1/README.md +0 -0
  31. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/examples/demo-data-1/config.yaml +0 -0
  32. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/examples/demo-data-2/README.md +0 -0
  33. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/examples/demo-data-2/config.yaml +0 -0
  34. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/examples/demo-data-3/leakage-current-results.json +0 -0
  35. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/examples/demo-data-3/overvoltage-results.json +0 -0
  36. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/examples/demo-data-3/product-xyz-compliance-report.json +0 -0
  37. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/examples/demo-data-3/product-xyz-specification.csv +0 -0
  38. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/examples/demo-data-3/thermal-response-results.json +0 -0
  39. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/examples/exercise-5-1-parametric-insights/README.md +0 -0
  40. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/examples/exercise-5-1-parametric-insights/config.yaml +0 -0
  41. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/examples/exercise-7-1-test-plans/README.md +0 -0
  42. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/examples/exercise-7-1-test-plans/config.yaml +0 -0
  43. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/examples/spec-compliance-notebooks/README.md +0 -0
  44. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/examples/spec-compliance-notebooks/config.yaml +0 -0
  45. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/examples/spec-compliance-notebooks/notebooks/SpecAnalysis_ComplianceCalculation.ipynb +0 -0
  46. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/examples/spec-compliance-notebooks/notebooks/SpecComplianceCalculation.ipynb +0 -0
  47. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/examples/spec-compliance-notebooks/notebooks/SpecfileExtractionAndIngestion.ipynb +0 -0
  48. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/examples/spec-compliance-notebooks/spec_template.xlsx +0 -0
  49. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/feed_click.py +0 -0
  50. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/file_click.py +0 -0
  51. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/function_click.py +0 -0
  52. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/function_templates.py +0 -0
  53. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/main.py +0 -0
  54. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/mcp_click.py +0 -0
  55. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/mcp_reachability.py +0 -0
  56. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/mcp_server.py +0 -0
  57. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/notebook_click.py +0 -0
  58. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/platform.py +0 -0
  59. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/policy_click.py +0 -0
  60. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/policy_utils.py +0 -0
  61. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/profiles.py +0 -0
  62. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/response_handlers.py +0 -0
  63. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/rich_output.py +0 -0
  64. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/routine_click.py +0 -0
  65. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/skill_click.py +0 -0
  66. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/skills/slcli/references/analysis-recipes.md +0 -0
  67. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/skills/slcli/references/commands.md +0 -0
  68. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/skills/slcli/references/datasheet-workflow.md +0 -0
  69. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/skills/slcli/references/filtering.md +0 -0
  70. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/skills/slcli/references/import-specs.min.json +0 -0
  71. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/skills/slcli/references/job-debugging/overview.md +0 -0
  72. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/skills/slcli/references/nipkg/overview.md +0 -0
  73. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/skills/slcli/references/notebook/interfaces.md +0 -0
  74. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/skills/slcli/references/notebook/notebook-patterns.md +0 -0
  75. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/skills/slcli/references/notebook/overview.md +0 -0
  76. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/skills/slcli/references/python-test/overview.md +0 -0
  77. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/skills/slcli/references/routine-examples.md +0 -0
  78. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/skills/slcli/references/troubleshooting.md +0 -0
  79. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/skills/slcli/references/webapp/angular-ui-packages.md +0 -0
  80. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/skills/slcli/references/webapp/deployment.md +0 -0
  81. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/skills/slcli/references/webapp/layout-patterns.md +0 -0
  82. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/skills/slcli/references/webapp/nimble-angular.md +0 -0
  83. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/skills/slcli/references/webapp/overview.md +0 -0
  84. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/skills/slcli/references/webapp/systemlink-services.md +0 -0
  85. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/skills/slcli/references/webapp/troubleshooting.md +0 -0
  86. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/skills/slcli/scripts/spec_import_helper.py +0 -0
  87. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/spec_click.py +0 -0
  88. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/ssl_trust.py +0 -0
  89. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/state_click.py +0 -0
  90. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/system_click.py +0 -0
  91. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/system_query_utils.py +0 -0
  92. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/table_utils.py +0 -0
  93. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/tag_click.py +0 -0
  94. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/templates_click.py +0 -0
  95. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/testmonitor_click.py +0 -0
  96. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/universal_handlers.py +0 -0
  97. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/user_click.py +0 -0
  98. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/utils.py +0 -0
  99. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/web_editor.py +0 -0
  100. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/webapp_templates/angular/blank/.gitignore +0 -0
  101. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/webapp_templates/angular/blank/README.md +0 -0
  102. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/webapp_templates/angular/blank/angular.json +0 -0
  103. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/webapp_templates/angular/blank/package.json +0 -0
  104. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/webapp_templates/angular/blank/src/app/app-routing.module.ts +0 -0
  105. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/webapp_templates/angular/blank/src/app/app.component.html +0 -0
  106. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/webapp_templates/angular/blank/src/app/app.component.scss +0 -0
  107. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/webapp_templates/angular/blank/src/app/app.component.ts +0 -0
  108. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/webapp_templates/angular/blank/src/app/app.module.ts +0 -0
  109. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/webapp_templates/angular/blank/src/app/core/layout/app-shell.component.html +0 -0
  110. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/webapp_templates/angular/blank/src/app/core/layout/app-shell.component.scss +0 -0
  111. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/webapp_templates/angular/blank/src/app/core/layout/app-shell.component.ts +0 -0
  112. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/webapp_templates/angular/blank/src/app/core/state/app-view-state.service.ts +0 -0
  113. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/webapp_templates/angular/blank/src/app/core/systemlink/systemlink-context.service.ts +0 -0
  114. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/webapp_templates/angular/blank/src/app/core/systemlink/theme-sync.service.ts +0 -0
  115. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/webapp_templates/angular/blank/src/app/core/systemlink/webapp-home-data.service.ts +0 -0
  116. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/webapp_templates/angular/blank/src/app/features/assets/assets-page.component.html +0 -0
  117. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/webapp_templates/angular/blank/src/app/features/assets/assets-page.component.scss +0 -0
  118. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/webapp_templates/angular/blank/src/app/features/assets/assets-page.component.ts +0 -0
  119. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/webapp_templates/angular/blank/src/app/features/datasets/datasets-page.component.html +0 -0
  120. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/webapp_templates/angular/blank/src/app/features/datasets/datasets-page.component.scss +0 -0
  121. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/webapp_templates/angular/blank/src/app/features/datasets/datasets-page.component.ts +0 -0
  122. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/webapp_templates/angular/blank/src/app/features/home/home-page.component.html +0 -0
  123. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/webapp_templates/angular/blank/src/app/features/home/home-page.component.scss +0 -0
  124. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/webapp_templates/angular/blank/src/app/features/home/home-page.component.ts +0 -0
  125. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/webapp_templates/angular/blank/src/app/features/master-detail/master-detail-page.component.html +0 -0
  126. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/webapp_templates/angular/blank/src/app/features/master-detail/master-detail-page.component.scss +0 -0
  127. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/webapp_templates/angular/blank/src/app/features/master-detail/master-detail-page.component.ts +0 -0
  128. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/webapp_templates/angular/blank/src/app/features/operations/operations-page.component.html +0 -0
  129. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/webapp_templates/angular/blank/src/app/features/operations/operations-page.component.scss +0 -0
  130. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/webapp_templates/angular/blank/src/app/features/operations/operations-page.component.ts +0 -0
  131. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/webapp_templates/angular/blank/src/app/features/settings/settings-page.component.html +0 -0
  132. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/webapp_templates/angular/blank/src/app/features/settings/settings-page.component.scss +0 -0
  133. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/webapp_templates/angular/blank/src/app/features/settings/settings-page.component.ts +0 -0
  134. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/webapp_templates/angular/blank/src/app/shared/components/empty-state.component.html +0 -0
  135. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/webapp_templates/angular/blank/src/app/shared/components/empty-state.component.scss +0 -0
  136. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/webapp_templates/angular/blank/src/app/shared/components/empty-state.component.ts +0 -0
  137. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/webapp_templates/angular/blank/src/app/shared/components/error-banner.component.html +0 -0
  138. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/webapp_templates/angular/blank/src/app/shared/components/error-banner.component.scss +0 -0
  139. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/webapp_templates/angular/blank/src/app/shared/components/error-banner.component.ts +0 -0
  140. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/webapp_templates/angular/blank/src/app/shared/components/loading-state.component.html +0 -0
  141. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/webapp_templates/angular/blank/src/app/shared/components/loading-state.component.scss +0 -0
  142. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/webapp_templates/angular/blank/src/app/shared/components/loading-state.component.ts +0 -0
  143. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/webapp_templates/angular/blank/src/app/shared/states/view-state.model.ts +0 -0
  144. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/webapp_templates/angular/blank/src/assets/README.md +0 -0
  145. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/webapp_templates/angular/blank/src/index.html +0 -0
  146. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/webapp_templates/angular/blank/src/main.ts +0 -0
  147. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/webapp_templates/angular/blank/src/styles.scss +0 -0
  148. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/webapp_templates/angular/blank/tsconfig.app.json +0 -0
  149. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/webapp_templates/angular/blank/tsconfig.json +0 -0
  150. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/workflow_preview.py +0 -0
  151. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/workflows_click.py +0 -0
  152. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/workitem_click.py +0 -0
  153. {systemlink_cli-1.22.0 → systemlink_cli-1.23.0}/slcli/workspace_click.py +0 -0
  154. {systemlink_cli-1.22.0 → systemlink_cli-1.23.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.22.0
3
+ Version: 1.23.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.22.0"
3
+ version = "1.23.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.22.0"
4
+ __version__ = "1.23.0"
@@ -37,6 +37,7 @@ class ExampleLoader:
37
37
  "tag",
38
38
  "specification",
39
39
  "feed",
40
+ "package",
40
41
  "alarm",
41
42
  }
42
43
 
@@ -7,6 +7,8 @@ Supports dry-run mode for validation without creating resources.
7
7
  from __future__ import annotations
8
8
 
9
9
  import json as json_module
10
+ import re
11
+ import tempfile
10
12
  import urllib.parse
11
13
  from dataclasses import dataclass
12
14
  from enum import Enum
@@ -16,7 +18,8 @@ from typing import Any, Dict, List, Optional, Tuple
16
18
  import click
17
19
  import requests
18
20
 
19
- from .utils import get_base_url, get_headers, make_api_request
21
+ from .utils import get_base_url, get_headers, get_ssl_verify, make_api_request, sanitize_filename
22
+ from .webapp_click import pack_folder_to_nipkg
20
23
 
21
24
 
22
25
  class ProvisioningAction(Enum):
@@ -216,6 +219,7 @@ class ExampleProvisioner:
216
219
  "tag": self._delete_tag,
217
220
  "specification": self._delete_specification,
218
221
  "feed": self._delete_feed,
222
+ "package": self._delete_package,
219
223
  "alarm": self._delete_alarm,
220
224
  }
221
225
  delete_fn = delete_map.get(rtype)
@@ -298,6 +302,7 @@ class ExampleProvisioner:
298
302
  "tag": self._create_tag,
299
303
  "specification": self._create_specification,
300
304
  "feed": self._create_feed,
305
+ "package": self._create_package,
301
306
  "alarm": self._create_alarm,
302
307
  }
303
308
 
@@ -374,6 +379,8 @@ class ExampleProvisioner:
374
379
  str(props_with_name.get("platform", "")) or None,
375
380
  ownership_marker=self._resource_ownership_marker(props_with_name),
376
381
  )
382
+ elif rtype == "package":
383
+ existing_id = self._get_package_by_identity(props_with_name)
377
384
  elif rtype == "alarm":
378
385
  alarm_id = str(
379
386
  props_with_name.get("alarm_id", props_with_name.get("alarmId", rname))
@@ -406,6 +413,18 @@ class ExampleProvisioner:
406
413
  server_id=str(existing_id),
407
414
  error=str(exc),
408
415
  )
416
+ elif rtype == "state":
417
+ try:
418
+ self._update_state(str(existing_id), props_with_name)
419
+ except Exception as exc:
420
+ return ProvisioningResult(
421
+ id_reference=rid,
422
+ resource_type=rtype,
423
+ resource_name=rname,
424
+ action=ProvisioningAction.FAILED,
425
+ server_id=str(existing_id),
426
+ error=str(exc),
427
+ )
409
428
  # Resource already exists, skip creation
410
429
  return ProvisioningResult(
411
430
  id_reference=rid,
@@ -514,6 +533,27 @@ class ExampleProvisioner:
514
533
  product_id = self._get_product_by_name(str(referenced_resource.get("name", "")))
515
534
  if product_id:
516
535
  resolved[key] = product_id
536
+
537
+ for key in ("feed_id", "feedId"):
538
+ value = resolved.get(key)
539
+ if not isinstance(value, str) or not value.startswith("${") or not value.endswith("}"):
540
+ continue
541
+ reference = value[2:-1]
542
+ referenced_resource = resources_by_reference.get(reference)
543
+ if not referenced_resource or referenced_resource.get("type") != "feed":
544
+ continue
545
+ feed_properties = referenced_resource.get("properties", {})
546
+ if not isinstance(feed_properties, dict):
547
+ feed_properties = {}
548
+ feed_properties = dict(feed_properties)
549
+ feed_properties["name"] = referenced_resource.get("name", "")
550
+ feed_id = self._get_feed_by_name(
551
+ str(feed_properties["name"]),
552
+ str(feed_properties.get("platform", "")) or None,
553
+ ownership_marker=self._resource_ownership_marker(feed_properties),
554
+ )
555
+ if feed_id:
556
+ resolved[key] = feed_id
517
557
  return resolved
518
558
 
519
559
  @staticmethod
@@ -539,8 +579,8 @@ class ExampleProvisioner:
539
579
  return f"slcli-example:{self.example_name}"
540
580
  return None
541
581
 
542
- def _create_state(self, props: Dict[str, Any]) -> Optional[str]:
543
- """Create a systems state and return its server ID."""
582
+ def _build_state_payload(self, props: Dict[str, Any]) -> Dict[str, Any]:
583
+ """Build a systems state payload, resolving fixture resource references."""
544
584
  payload: Dict[str, Any] = {
545
585
  "name": props.get("name", ""),
546
586
  "distribution": props.get("distribution", ""),
@@ -551,14 +591,17 @@ class ExampleProvisioner:
551
591
 
552
592
  for source_key, api_key in (
553
593
  ("description", "description"),
554
- ("feeds", "feeds"),
555
- ("packages", "packages"),
556
594
  ("system_image", "systemImage"),
557
595
  ("systemImage", "systemImage"),
558
596
  ):
559
597
  if source_key in props:
560
598
  payload[api_key] = props[source_key]
561
599
 
600
+ if "feeds" in props:
601
+ payload["feeds"] = self._materialize_state_feeds(props["feeds"])
602
+ if "packages" in props:
603
+ payload["packages"] = self._materialize_state_packages(props["packages"])
604
+
562
605
  state_properties = props.get("properties")
563
606
  if isinstance(state_properties, dict):
564
607
  state_properties = dict(state_properties)
@@ -570,6 +613,112 @@ class ExampleProvisioner:
570
613
  if state_properties:
571
614
  payload["properties"] = state_properties
572
615
 
616
+ return payload
617
+
618
+ def _materialize_state_feeds(self, values: Any) -> Any:
619
+ """Resolve feed IDs into the structured feed objects required by states."""
620
+ if values is None:
621
+ return None
622
+ if not isinstance(values, list):
623
+ raise ValueError("State feeds must be a list")
624
+ return [self._materialize_state_feed(value) for value in values]
625
+
626
+ def _materialize_state_feed(self, value: Any) -> Dict[str, Any]:
627
+ """Resolve one state feed reference or preserve an explicit feed object."""
628
+ if isinstance(value, str):
629
+ return self._state_feed_from_id(value)
630
+ if not isinstance(value, dict):
631
+ raise ValueError("State feed entries must be references or objects")
632
+
633
+ reference = value.get("reference")
634
+ if reference is None:
635
+ return dict(value)
636
+ if not isinstance(reference, str) or not reference:
637
+ raise ValueError("State feed reference must be a non-empty string")
638
+
639
+ feed = self._state_feed_from_id(reference)
640
+ for key in ("name", "url", "enabled", "compressed"):
641
+ if key in value:
642
+ feed[key] = value[key]
643
+ return feed
644
+
645
+ def _state_feed_from_id(self, feed_id: str) -> Dict[str, Any]:
646
+ """Build a state feed object from a provisioned feed ID."""
647
+ from .feed_click import _get_feed, _get_feed_base_url
648
+
649
+ feed_data = _get_feed(feed_id)
650
+ feed_name = feed_data.get("name") or feed_data.get("feedName")
651
+ if not feed_name:
652
+ raise ValueError(f"Feed {feed_id} has no name")
653
+
654
+ feed_url = (
655
+ feed_data.get("url")
656
+ or feed_data.get("directoryUri")
657
+ or feed_data.get("directoryURI")
658
+ or f"{_get_feed_base_url()}/feeds/{urllib.parse.quote(feed_id, safe='')}/files"
659
+ )
660
+ return {
661
+ "name": str(feed_name),
662
+ "url": str(feed_url),
663
+ "enabled": True,
664
+ "compressed": False,
665
+ }
666
+
667
+ def _materialize_state_packages(self, values: Any) -> Any:
668
+ """Resolve package IDs into the structured package objects required by states."""
669
+ if values is None:
670
+ return None
671
+ if not isinstance(values, list):
672
+ raise ValueError("State packages must be a list")
673
+ return [self._materialize_state_package(value) for value in values]
674
+
675
+ def _materialize_state_package(self, value: Any) -> Dict[str, Any]:
676
+ """Resolve one state package reference or preserve an explicit package object."""
677
+ if isinstance(value, str):
678
+ return self._state_package_from_id(value)
679
+ if not isinstance(value, dict):
680
+ raise ValueError("State package entries must be references or objects")
681
+
682
+ reference = value.get("reference")
683
+ if reference is None:
684
+ return dict(value)
685
+ if not isinstance(reference, str) or not reference:
686
+ raise ValueError("State package reference must be a non-empty string")
687
+
688
+ package = self._state_package_from_id(reference)
689
+ if "installRecommends" in value:
690
+ package["installRecommends"] = value["installRecommends"]
691
+ return package
692
+
693
+ def _state_package_from_id(self, package_id: str) -> Dict[str, Any]:
694
+ """Build a state package object from a provisioned package ID."""
695
+ from .feed_click import _get_feed_base_url
696
+
697
+ package_url = f"{_get_feed_base_url()}/packages/{urllib.parse.quote(package_id, safe='')}"
698
+ resp = make_api_request("GET", package_url, payload=None, handle_errors=False)
699
+ data = resp.json()
700
+ if isinstance(data, dict) and isinstance(data.get("package"), dict):
701
+ data = data["package"]
702
+ if not isinstance(data, dict):
703
+ raise ValueError(f"Package {package_id} response is invalid")
704
+
705
+ metadata = data.get("metadata", {})
706
+ if not isinstance(metadata, dict):
707
+ metadata = {}
708
+ package_name = metadata.get("packageName") or metadata.get("name") or data.get("name")
709
+ version = metadata.get("version") or data.get("version")
710
+ if not package_name or not version:
711
+ raise ValueError(f"Package {package_id} has incomplete metadata")
712
+ return {
713
+ "name": str(package_name),
714
+ "version": str(version),
715
+ "installRecommends": True,
716
+ }
717
+
718
+ def _create_state(self, props: Dict[str, Any]) -> Optional[str]:
719
+ """Create a systems state and return its server ID."""
720
+ payload = self._build_state_payload(props)
721
+
573
722
  resp = make_api_request(
574
723
  "POST",
575
724
  f"{get_base_url()}/nisystemsstate/v1/states",
@@ -581,6 +730,15 @@ class ExampleProvisioner:
581
730
  return None
582
731
  return str(data["id"])
583
732
 
733
+ def _update_state(self, state_id: str, props: Dict[str, Any]) -> None:
734
+ """Update an existing systems state to match the fixture configuration."""
735
+ make_api_request(
736
+ "PATCH",
737
+ f"{get_base_url()}/nisystemsstate/v1/states/{state_id}",
738
+ self._build_state_payload(props),
739
+ handle_errors=False,
740
+ )
741
+
584
742
  def _get_state_by_name(
585
743
  self, name: str, ownership_marker: Optional[str] = None
586
744
  ) -> Optional[str]:
@@ -1187,6 +1345,217 @@ class ExampleProvisioner:
1187
1345
  _wait_for_job(str(job_id), timeout=int(props.get("timeout", 300)))
1188
1346
  return feed_id
1189
1347
 
1348
+ def _package_identity(self, props: Dict[str, Any]) -> Tuple[str, str, str]:
1349
+ """Return the feed, package name, and version used to identify a package."""
1350
+ feed_id = str(props.get("feed_id", props.get("feedId", "")))
1351
+ source = props.get("source", {})
1352
+ if not isinstance(source, dict):
1353
+ source = {}
1354
+ package_name = sanitize_filename(
1355
+ str(
1356
+ source.get(
1357
+ "package_name",
1358
+ source.get(
1359
+ "package", props.get("package_name", props.get("name", "fixture-package"))
1360
+ ),
1361
+ )
1362
+ ),
1363
+ "fixture-package",
1364
+ )
1365
+ version = self._safe_package_component(
1366
+ str(source.get("version", props.get("version", "1.0.0"))), "1.0.0"
1367
+ )
1368
+ return feed_id, package_name, version
1369
+
1370
+ def _get_package_by_identity(self, props: Dict[str, Any]) -> Optional[str]:
1371
+ """Find an example package by feed, package name, and version."""
1372
+ from .feed_click import _list_packages
1373
+
1374
+ feed_id, package_name, version = self._package_identity(props)
1375
+ if not feed_id or not package_name or not version:
1376
+ return None
1377
+
1378
+ try:
1379
+ packages = _list_packages(feed_id)
1380
+ except Exception:
1381
+ return None
1382
+
1383
+ for package in packages:
1384
+ metadata = package.get("metadata", {})
1385
+ if not isinstance(metadata, dict):
1386
+ metadata = {}
1387
+ actual_name = metadata.get("packageName", metadata.get("name", package.get("name")))
1388
+ actual_version = metadata.get("version", package.get("version"))
1389
+ if str(actual_name) == package_name and str(actual_version) == version:
1390
+ package_id = package.get("id", package.get("packageId"))
1391
+ if package_id:
1392
+ return str(package_id)
1393
+ return None
1394
+
1395
+ @staticmethod
1396
+ def _safe_package_component(value: str, fallback: str) -> str:
1397
+ """Convert a package name component into a safe folder-name component."""
1398
+ normalized = re.sub(r"[^A-Za-z0-9._-]+", "-", value).strip("-._")
1399
+ return normalized or fallback
1400
+
1401
+ def _write_dummy_package_payload(self, folder: Path, files: Any) -> None:
1402
+ """Write inline dummy package files while preventing path escapes."""
1403
+ if files is None:
1404
+ files = {"README.txt": "Package created by slcli example provisioner\n"}
1405
+ if not isinstance(files, dict):
1406
+ raise ValueError("Dummy package source 'files' must be an object")
1407
+
1408
+ root = folder.resolve()
1409
+ for relative_name, content in files.items():
1410
+ if not isinstance(relative_name, str) or not relative_name.strip():
1411
+ raise ValueError("Dummy package file names must be non-empty strings")
1412
+ destination = (root / relative_name).resolve()
1413
+ try:
1414
+ destination.relative_to(root)
1415
+ except ValueError as exc:
1416
+ raise ValueError(
1417
+ f"Dummy package file must remain inside the package folder: {relative_name}"
1418
+ ) from exc
1419
+ destination.parent.mkdir(parents=True, exist_ok=True)
1420
+ if isinstance(content, str):
1421
+ destination.write_text(content, encoding="utf-8")
1422
+ elif isinstance(content, bytes):
1423
+ destination.write_bytes(content)
1424
+ else:
1425
+ raise ValueError(f"Dummy package content must be text or bytes: {relative_name}")
1426
+
1427
+ def _materialize_package(self, props: Dict[str, Any], output_dir: Path) -> Path:
1428
+ """Create or resolve the package file described by a package resource."""
1429
+ source = props.get("source", {"type": "dummy"})
1430
+ if not isinstance(source, dict):
1431
+ raise ValueError("Package source must be an object")
1432
+ source_type = str(source.get("type", "dummy")).lower()
1433
+
1434
+ if source_type == "file":
1435
+ file_path = source.get("path", source.get("file"))
1436
+ if not isinstance(file_path, str) or not file_path:
1437
+ raise ValueError("File package source requires 'path'")
1438
+ resolved_file = self._resolve_example_file(file_path)
1439
+ if resolved_file is None or not resolved_file.is_file():
1440
+ raise FileNotFoundError(f"Package file not found: {file_path}")
1441
+ if resolved_file.suffix.lower() != ".nipkg":
1442
+ raise ValueError("Package file source must point to a .nipkg file")
1443
+ return resolved_file
1444
+
1445
+ if source_type == "repository":
1446
+ package_url = source.get("url", source.get("package_url"))
1447
+ if not isinstance(package_url, str) or not package_url:
1448
+ raise ValueError("Repository package source requires 'url'")
1449
+ parsed = urllib.parse.urlparse(package_url)
1450
+ if parsed.scheme not in {"http", "https"} or not parsed.netloc:
1451
+ raise ValueError("Repository package URL must use http or https")
1452
+ filename = Path(parsed.path).name or "repository-package.nipkg"
1453
+ if not filename.lower().endswith(".nipkg"):
1454
+ raise ValueError("Repository package URL must point to a .nipkg file")
1455
+ output_dir.mkdir(parents=True, exist_ok=True)
1456
+ destination = output_dir / filename
1457
+ max_size_bytes = int(source.get("max_size_bytes", 1024 * 1024 * 1024))
1458
+ if max_size_bytes <= 0:
1459
+ raise ValueError("Repository package 'max_size_bytes' must be positive")
1460
+ response = requests.get(
1461
+ package_url,
1462
+ stream=True,
1463
+ timeout=int(source.get("timeout", props.get("timeout", 300))),
1464
+ verify=get_ssl_verify(package_url),
1465
+ )
1466
+ try:
1467
+ response.raise_for_status()
1468
+ content_length = response.headers.get("Content-Length")
1469
+ if content_length and int(content_length) > max_size_bytes:
1470
+ raise ValueError("Repository package exceeds the configured size limit")
1471
+ downloaded_bytes = 0
1472
+ with open(destination, "wb") as package_file:
1473
+ for chunk in response.iter_content(chunk_size=8192):
1474
+ if chunk:
1475
+ downloaded_bytes += len(chunk)
1476
+ if downloaded_bytes > max_size_bytes:
1477
+ raise ValueError(
1478
+ "Repository package exceeds the configured size limit"
1479
+ )
1480
+ package_file.write(chunk)
1481
+ except Exception:
1482
+ destination.unlink(missing_ok=True)
1483
+ raise
1484
+ finally:
1485
+ response.close()
1486
+ return destination
1487
+
1488
+ if source_type != "dummy":
1489
+ raise ValueError(f"Unsupported package source type: {source_type}")
1490
+
1491
+ package_name = sanitize_filename(
1492
+ str(
1493
+ source.get(
1494
+ "package_name",
1495
+ source.get(
1496
+ "package", props.get("package_name", props.get("name", "fixture-package"))
1497
+ ),
1498
+ )
1499
+ ),
1500
+ "fixture-package",
1501
+ )
1502
+ version = self._safe_package_component(
1503
+ str(source.get("version", props.get("version", "1.0.0"))), "1.0.0"
1504
+ )
1505
+ architecture = self._safe_package_component(str(source.get("architecture", "all")), "all")
1506
+ package_folder = output_dir / f"{package_name}_{version}_{architecture}"
1507
+ package_folder.mkdir(parents=True, exist_ok=True)
1508
+ self._write_dummy_package_payload(package_folder, source.get("files"))
1509
+ return pack_folder_to_nipkg(package_folder)
1510
+
1511
+ def _create_package(self, props: Dict[str, Any]) -> Optional[str]:
1512
+ """Create a package file and upload it to an existing feed."""
1513
+ from .feed_click import _list_packages, _upload_package, _wait_for_job
1514
+
1515
+ feed_id = str(props.get("feed_id", props.get("feedId", "")))
1516
+ if not feed_id:
1517
+ return None
1518
+
1519
+ with tempfile.TemporaryDirectory(prefix="slcli-example-package-") as temp_dir:
1520
+ package_path = self._materialize_package(props, Path(temp_dir))
1521
+ upload_result = _upload_package(
1522
+ feed_id,
1523
+ str(package_path),
1524
+ overwrite=bool(props.get("overwrite", False)),
1525
+ )
1526
+ job_id = upload_result.get("jobId", upload_result.get("job", {}).get("id", ""))
1527
+ package_id = upload_result.get("id", upload_result.get("packageId", ""))
1528
+ if job_id:
1529
+ completed_job = _wait_for_job(
1530
+ str(job_id),
1531
+ timeout=int(props.get("timeout", 300)),
1532
+ feed_id=feed_id,
1533
+ )
1534
+ package_id = completed_job.get("resourceId", package_id)
1535
+ if not package_id:
1536
+ package_id = self._get_package_by_identity(props)
1537
+ if not package_id:
1538
+ for package in _list_packages(feed_id):
1539
+ if Path(str(package.get("fileName", ""))).name == package_path.name:
1540
+ package_id = package.get("id", package.get("packageId", ""))
1541
+ break
1542
+ if not package_id:
1543
+ return None
1544
+ self._last_resource_details = {"file": package_path.name, "feed_id": feed_id}
1545
+ return str(package_id)
1546
+
1547
+ def _delete_package(self, props: Dict[str, Any]) -> Optional[str]:
1548
+ """Delete an example package by its feed identity."""
1549
+ from .feed_click import _delete_package as delete_package, _wait_for_job
1550
+
1551
+ package_id = self._get_package_by_identity(props)
1552
+ if not package_id:
1553
+ return None
1554
+ job_id = delete_package(package_id)
1555
+ if job_id:
1556
+ _wait_for_job(str(job_id), timeout=int(props.get("timeout", 300)))
1557
+ return package_id
1558
+
1190
1559
  # --- Create methods (real API calls) ---
1191
1560
  def _create_location(self, props: Dict[str, Any]) -> str:
1192
1561
  """Create location via /nilocation/v1/locations API and return server ID."""
@@ -46,10 +46,9 @@ discover through visualization:
46
46
 
47
47
  Phase-one fixture data for validating Systems and Products queries. It
48
48
  provisions the resource families supported by the generic example provisioner:
49
- 1 deployment state, 1 feed, 1 tag with 12 history samples, 3 active alarms,
50
- and 1 specification (metadata only). It reports unsupported package inventory,
51
- jobs, feed packages, deployment state package inventory, specification
52
- evidence, and workspace lifecycle.
49
+ 1 deployment state populated with that feed and both package versions, 1 tag
50
+ with 12 history samples, 3 active alarms, and 1 specification (metadata only).
51
+ It reports unsupported jobs, specification evidence, and workspace lifecycle.
53
52
  Because those capabilities are required by the full Nigel acceptance matrix,
54
53
  the install intentionally exits nonzero after emitting its JSON manifest.
55
54
 
@@ -183,12 +182,55 @@ cleanup:
183
182
  - `data_table` - DataFrame table schema
184
183
  - `file` - Uploaded supporting file
185
184
  - `notebook` - Uploaded Jupyter notebook
186
- - `feed` - Package feed metadata (package uploads are separate)
185
+ - `feed` - Package feed metadata
186
+ - `package` - Package uploaded to a previously created feed
187
187
  - `state` - Systems deployment state
188
188
  - `tag` - Workspace-scoped tag metadata
189
189
  - `specification` - Product specification metadata
190
190
  - `alarm` - Active alarm instance and transition
191
191
 
192
+ ### Package Resources
193
+
194
+ Package resources must follow their feed in the resource list so that the feed
195
+ reference can be resolved:
196
+
197
+ ```yaml
198
+ - type: "feed"
199
+ name: "Fixture Windows Feed"
200
+ properties:
201
+ platform: "windows"
202
+ id_reference: "feed_fixture"
203
+
204
+ - type: "package"
205
+ name: "Fixture package"
206
+ properties:
207
+ feed_id: "${feed_fixture}"
208
+ source:
209
+ type: "dummy"
210
+ package_name: "fixture-package"
211
+ version: "1.0.0"
212
+ architecture: "all"
213
+ files:
214
+ README.txt: "Package payload created for fixture validation.\n"
215
+ id_reference: "package_fixture"
216
+ ```
217
+
218
+ Supported package sources are:
219
+
220
+ - `dummy` (default): creates a deterministic minimal `.nipkg` from inline
221
+ `files` and uploads it to the feed.
222
+ - `file`: uploads a fixture-relative `.nipkg` from `source.path`. The path is
223
+ contained within the directory containing `config.yaml`.
224
+ - `repository`: downloads a direct HTTP(S) URL ending in `.nipkg`, then uploads
225
+ it. Use a URL obtained from the repository/feed service; the repository
226
+ catalog is not treated as a package-byte endpoint. Downloads are streamed and
227
+ TLS verification remains enabled.
228
+
229
+ `source.package_name` and `source.version` identify a package for duplicate
230
+ detection across all source modes. Dummy packages default to the resource name
231
+ and version `1.0.0`; declare both values for local or repository packages when
232
+ they cannot be inferred from the resource name.
233
+
192
234
  ## Notes
193
235
 
194
236
  - Examples are versioned with the `format_version` field (currently 1.0)
@@ -197,6 +239,7 @@ cleanup:
197
239
  consume its custom order or confirmation settings.
198
240
  - References use `${id_reference}` syntax for interpolation
199
241
  - Resources are provisioned in list order, so referenced resources must appear first
242
+ - Package resources are provisioned after their feed and are deleted before it
200
243
  - All resources created by an example are tagged with the example name for safe deletion
201
244
  - The JSON Schema is the authoring reference; runtime validation currently checks only a subset of its types and patterns
202
245
  - Examples with `install_manifest: true` emit grouped resource actions and a
@@ -179,6 +179,7 @@
179
179
  "tag",
180
180
  "specification",
181
181
  "feed",
182
+ "package",
182
183
  "alarm"
183
184
  ],
184
185
  "description": "SLE resource type"
@@ -189,7 +190,7 @@
189
190
  },
190
191
  "properties": {
191
192
  "type": "object",
192
- "description": "Resource-specific properties (API fields)"
193
+ "description": "Resource-specific properties (API fields). For package resources, use feed_id and source with type dummy, file, or repository."
193
194
  },
194
195
  "id_reference": {
195
196
  "type": "string",
@@ -8,14 +8,15 @@ description: |
8
8
 
9
9
  This release provisions the resource families supported by the generic
10
10
  example provisioner: products, virtual systems, DUT and instrument assets,
11
- test results with steps, a deployment state, feed and tag metadata/history, a
12
- specification condition metadata, populated DataFrame tables, and supporting files.
11
+ test results with steps, a deployment state, feed and package metadata,
12
+ tag metadata/history, specification condition metadata, populated DataFrame
13
+ tables, and supporting files.
13
14
  Unsupported capabilities are declared below and make the install exit with a
14
15
  nonzero status. The installer must not present this phase-one fixture as the
15
16
  complete 14-example acceptance dataset.
16
17
  author: "NI"
17
18
  created_date: "2026-08-05"
18
- updated_date: "2026-08-07"
19
+ updated_date: "2026-08-11"
19
20
  tags:
20
21
  - "nigel"
21
22
  - "query-fixture"
@@ -38,8 +39,6 @@ validation:
38
39
  - "workspace lifecycle: example install requires an existing workspace and does not create or delete it"
39
40
  - "system package inventory: virtual-system creation does not seed enumerable packages or versions"
40
41
  - "system jobs: the generic example provisioner has no queued/running/failed job seed path"
41
- - "feed packages: feed creation and package upload are asynchronous CLI workflows, not fixture resources"
42
- - "deployment state package inventory: state creation does not seed package inventory"
43
42
  - "specification condition evidence: /nispec/v1 stores expected conditions only; it has no outcome or test-result evidence API"
44
43
  - "system-to-asset query projection: location.minionId is provisioned, but the accepted CLI/Nigel retrieval path is not verified"
45
44
  - "result-to-instrument query projection: asset ID and channel fields are provisioned, but the accepted CLI/Nigel retrieval path is not verified"
@@ -73,12 +72,47 @@ resources:
73
72
  id_reference: "feed_nigel_windows"
74
73
  tags: ["demo-data-3"]
75
74
 
75
+ - type: "package"
76
+ name: "Nigel Fixture Agent"
77
+ properties:
78
+ feed_id: "${feed_nigel_windows}"
79
+ source:
80
+ type: "dummy"
81
+ package_name: "nigel-fixture-agent"
82
+ version: "1.0.0"
83
+ architecture: "all"
84
+ files:
85
+ README.txt: "Deterministic fixture package for Systems query validation.\n"
86
+ package-info.txt: "nigel-fixture-agent 1.0.0\n"
87
+ id_reference: "package_nigel_agent"
88
+ tags: ["demo-data-3"]
89
+
90
+ - type: "package"
91
+ name: "Nigel Fixture Agent Update"
92
+ properties:
93
+ feed_id: "${feed_nigel_windows}"
94
+ source:
95
+ type: "dummy"
96
+ package_name: "nigel-fixture-agent"
97
+ version: "1.1.0"
98
+ architecture: "all"
99
+ files:
100
+ README.txt: "Deterministic fixture package update for Systems query validation.\n"
101
+ package-info.txt: "nigel-fixture-agent 1.1.0\n"
102
+ id_reference: "package_nigel_agent_update"
103
+ tags: ["demo-data-3"]
104
+
76
105
  - type: "state"
77
106
  name: "Nigel Fixture Windows State"
78
107
  properties:
79
108
  description: "Windows deployment state for query fixtures"
80
109
  distribution: "WINDOWS"
81
110
  architecture: "X64"
111
+ feeds:
112
+ - "${feed_nigel_windows}"
113
+ packages:
114
+ - "${package_nigel_agent}"
115
+ - "${package_nigel_agent_update}"
82
116
  properties:
83
117
  fixture: "demo-data-3"
84
118
  id_reference: "state_nigel_windows"