systemlink-cli 1.13.12__tar.gz → 1.13.14__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 (93) hide show
  1. {systemlink_cli-1.13.12 → systemlink_cli-1.13.14}/PKG-INFO +1 -1
  2. {systemlink_cli-1.13.12 → systemlink_cli-1.13.14}/pyproject.toml +1 -1
  3. {systemlink_cli-1.13.12 → systemlink_cli-1.13.14}/slcli/_version.py +1 -1
  4. {systemlink_cli-1.13.12 → systemlink_cli-1.13.14}/slcli/testmonitor_click.py +25 -13
  5. {systemlink_cli-1.13.12 → systemlink_cli-1.13.14}/slcli/utils.py +62 -7
  6. {systemlink_cli-1.13.12 → systemlink_cli-1.13.14}/LICENSE +0 -0
  7. {systemlink_cli-1.13.12 → systemlink_cli-1.13.14}/dff-editor/editor.js +0 -0
  8. {systemlink_cli-1.13.12 → systemlink_cli-1.13.14}/dff-editor/index.html +0 -0
  9. {systemlink_cli-1.13.12 → systemlink_cli-1.13.14}/slcli/__init__.py +0 -0
  10. {systemlink_cli-1.13.12 → systemlink_cli-1.13.14}/slcli/__main__.py +0 -0
  11. {systemlink_cli-1.13.12 → systemlink_cli-1.13.14}/slcli/asset_click.py +0 -0
  12. {systemlink_cli-1.13.12 → systemlink_cli-1.13.14}/slcli/cli_formatters.py +0 -0
  13. {systemlink_cli-1.13.12 → systemlink_cli-1.13.14}/slcli/cli_utils.py +0 -0
  14. {systemlink_cli-1.13.12 → systemlink_cli-1.13.14}/slcli/comment_click.py +0 -0
  15. {systemlink_cli-1.13.12 → systemlink_cli-1.13.14}/slcli/completion_click.py +0 -0
  16. {systemlink_cli-1.13.12 → systemlink_cli-1.13.14}/slcli/config.py +0 -0
  17. {systemlink_cli-1.13.12 → systemlink_cli-1.13.14}/slcli/config_click.py +0 -0
  18. {systemlink_cli-1.13.12 → systemlink_cli-1.13.14}/slcli/dataframe_click.py +0 -0
  19. {systemlink_cli-1.13.12 → systemlink_cli-1.13.14}/slcli/dff_click.py +0 -0
  20. {systemlink_cli-1.13.12 → systemlink_cli-1.13.14}/slcli/dff_decorators.py +0 -0
  21. {systemlink_cli-1.13.12 → systemlink_cli-1.13.14}/slcli/example_click.py +0 -0
  22. {systemlink_cli-1.13.12 → systemlink_cli-1.13.14}/slcli/example_loader.py +0 -0
  23. {systemlink_cli-1.13.12 → systemlink_cli-1.13.14}/slcli/example_provisioner.py +0 -0
  24. {systemlink_cli-1.13.12 → systemlink_cli-1.13.14}/slcli/examples/README.md +0 -0
  25. {systemlink_cli-1.13.12 → systemlink_cli-1.13.14}/slcli/examples/_schema/schema-v1.0.json +0 -0
  26. {systemlink_cli-1.13.12 → systemlink_cli-1.13.14}/slcli/examples/demo-complete-workflow/README.md +0 -0
  27. {systemlink_cli-1.13.12 → systemlink_cli-1.13.14}/slcli/examples/demo-complete-workflow/config.yaml +0 -0
  28. {systemlink_cli-1.13.12 → systemlink_cli-1.13.14}/slcli/examples/demo-test-plans/README.md +0 -0
  29. {systemlink_cli-1.13.12 → systemlink_cli-1.13.14}/slcli/examples/demo-test-plans/config.yaml +0 -0
  30. {systemlink_cli-1.13.12 → systemlink_cli-1.13.14}/slcli/examples/exercise-5-1-parametric-insights/README.md +0 -0
  31. {systemlink_cli-1.13.12 → systemlink_cli-1.13.14}/slcli/examples/exercise-5-1-parametric-insights/config.yaml +0 -0
  32. {systemlink_cli-1.13.12 → systemlink_cli-1.13.14}/slcli/examples/exercise-7-1-test-plans/README.md +0 -0
  33. {systemlink_cli-1.13.12 → systemlink_cli-1.13.14}/slcli/examples/exercise-7-1-test-plans/config.yaml +0 -0
  34. {systemlink_cli-1.13.12 → systemlink_cli-1.13.14}/slcli/examples/spec-compliance-notebooks/README.md +0 -0
  35. {systemlink_cli-1.13.12 → systemlink_cli-1.13.14}/slcli/examples/spec-compliance-notebooks/config.yaml +0 -0
  36. {systemlink_cli-1.13.12 → systemlink_cli-1.13.14}/slcli/examples/spec-compliance-notebooks/notebooks/SpecAnalysis_ComplianceCalculation.ipynb +0 -0
  37. {systemlink_cli-1.13.12 → systemlink_cli-1.13.14}/slcli/examples/spec-compliance-notebooks/notebooks/SpecComplianceCalculation.ipynb +0 -0
  38. {systemlink_cli-1.13.12 → systemlink_cli-1.13.14}/slcli/examples/spec-compliance-notebooks/notebooks/SpecfileExtractionAndIngestion.ipynb +0 -0
  39. {systemlink_cli-1.13.12 → systemlink_cli-1.13.14}/slcli/examples/spec-compliance-notebooks/spec_template.xlsx +0 -0
  40. {systemlink_cli-1.13.12 → systemlink_cli-1.13.14}/slcli/feed_click.py +0 -0
  41. {systemlink_cli-1.13.12 → systemlink_cli-1.13.14}/slcli/file_click.py +0 -0
  42. {systemlink_cli-1.13.12 → systemlink_cli-1.13.14}/slcli/function_click.py +0 -0
  43. {systemlink_cli-1.13.12 → systemlink_cli-1.13.14}/slcli/function_templates.py +0 -0
  44. {systemlink_cli-1.13.12 → systemlink_cli-1.13.14}/slcli/main.py +0 -0
  45. {systemlink_cli-1.13.12 → systemlink_cli-1.13.14}/slcli/mcp_click.py +0 -0
  46. {systemlink_cli-1.13.12 → systemlink_cli-1.13.14}/slcli/mcp_reachability.py +0 -0
  47. {systemlink_cli-1.13.12 → systemlink_cli-1.13.14}/slcli/mcp_server.py +0 -0
  48. {systemlink_cli-1.13.12 → systemlink_cli-1.13.14}/slcli/notebook_click.py +0 -0
  49. {systemlink_cli-1.13.12 → systemlink_cli-1.13.14}/slcli/platform.py +0 -0
  50. {systemlink_cli-1.13.12 → systemlink_cli-1.13.14}/slcli/policy_click.py +0 -0
  51. {systemlink_cli-1.13.12 → systemlink_cli-1.13.14}/slcli/policy_utils.py +0 -0
  52. {systemlink_cli-1.13.12 → systemlink_cli-1.13.14}/slcli/profiles.py +0 -0
  53. {systemlink_cli-1.13.12 → systemlink_cli-1.13.14}/slcli/response_handlers.py +0 -0
  54. {systemlink_cli-1.13.12 → systemlink_cli-1.13.14}/slcli/rich_output.py +0 -0
  55. {systemlink_cli-1.13.12 → systemlink_cli-1.13.14}/slcli/routine_click.py +0 -0
  56. {systemlink_cli-1.13.12 → systemlink_cli-1.13.14}/slcli/skill_click.py +0 -0
  57. {systemlink_cli-1.13.12 → systemlink_cli-1.13.14}/slcli/skills/nipkg-file-package/SKILL.md +0 -0
  58. {systemlink_cli-1.13.12 → systemlink_cli-1.13.14}/slcli/skills/slcli/SKILL.md +0 -0
  59. {systemlink_cli-1.13.12 → systemlink_cli-1.13.14}/slcli/skills/slcli/references/analysis-recipes.md +0 -0
  60. {systemlink_cli-1.13.12 → systemlink_cli-1.13.14}/slcli/skills/slcli/references/commands.md +0 -0
  61. {systemlink_cli-1.13.12 → systemlink_cli-1.13.14}/slcli/skills/slcli/references/datasheet-workflow.md +0 -0
  62. {systemlink_cli-1.13.12 → systemlink_cli-1.13.14}/slcli/skills/slcli/references/filtering.md +0 -0
  63. {systemlink_cli-1.13.12 → systemlink_cli-1.13.14}/slcli/skills/slcli/references/routine-examples.md +0 -0
  64. {systemlink_cli-1.13.12 → systemlink_cli-1.13.14}/slcli/skills/slcli/references/troubleshooting.md +0 -0
  65. {systemlink_cli-1.13.12 → systemlink_cli-1.13.14}/slcli/skills/systemlink-job-debugging/SKILL.md +0 -0
  66. {systemlink_cli-1.13.12 → systemlink_cli-1.13.14}/slcli/skills/systemlink-notebook/SKILL.md +0 -0
  67. {systemlink_cli-1.13.12 → systemlink_cli-1.13.14}/slcli/skills/systemlink-notebook/references/interfaces.md +0 -0
  68. {systemlink_cli-1.13.12 → systemlink_cli-1.13.14}/slcli/skills/systemlink-notebook/references/notebook-patterns.md +0 -0
  69. {systemlink_cli-1.13.12 → systemlink_cli-1.13.14}/slcli/skills/systemlink-python-test/SKILL.md +0 -0
  70. {systemlink_cli-1.13.12 → systemlink_cli-1.13.14}/slcli/skills/systemlink-webapp/SKILL.md +0 -0
  71. {systemlink_cli-1.13.12 → systemlink_cli-1.13.14}/slcli/skills/systemlink-webapp/references/angular-ui-packages.md +0 -0
  72. {systemlink_cli-1.13.12 → systemlink_cli-1.13.14}/slcli/skills/systemlink-webapp/references/deployment.md +0 -0
  73. {systemlink_cli-1.13.12 → systemlink_cli-1.13.14}/slcli/skills/systemlink-webapp/references/layout-patterns.md +0 -0
  74. {systemlink_cli-1.13.12 → systemlink_cli-1.13.14}/slcli/skills/systemlink-webapp/references/nimble-angular.md +0 -0
  75. {systemlink_cli-1.13.12 → systemlink_cli-1.13.14}/slcli/skills/systemlink-webapp/references/systemlink-services.md +0 -0
  76. {systemlink_cli-1.13.12 → systemlink_cli-1.13.14}/slcli/skills/systemlink-webapp/references/troubleshooting.md +0 -0
  77. {systemlink_cli-1.13.12 → systemlink_cli-1.13.14}/slcli/spec_click.py +0 -0
  78. {systemlink_cli-1.13.12 → systemlink_cli-1.13.14}/slcli/ssl_trust.py +0 -0
  79. {systemlink_cli-1.13.12 → systemlink_cli-1.13.14}/slcli/state_click.py +0 -0
  80. {systemlink_cli-1.13.12 → systemlink_cli-1.13.14}/slcli/system_click.py +0 -0
  81. {systemlink_cli-1.13.12 → systemlink_cli-1.13.14}/slcli/system_query_utils.py +0 -0
  82. {systemlink_cli-1.13.12 → systemlink_cli-1.13.14}/slcli/table_utils.py +0 -0
  83. {systemlink_cli-1.13.12 → systemlink_cli-1.13.14}/slcli/tag_click.py +0 -0
  84. {systemlink_cli-1.13.12 → systemlink_cli-1.13.14}/slcli/templates_click.py +0 -0
  85. {systemlink_cli-1.13.12 → systemlink_cli-1.13.14}/slcli/universal_handlers.py +0 -0
  86. {systemlink_cli-1.13.12 → systemlink_cli-1.13.14}/slcli/user_click.py +0 -0
  87. {systemlink_cli-1.13.12 → systemlink_cli-1.13.14}/slcli/web_editor.py +0 -0
  88. {systemlink_cli-1.13.12 → systemlink_cli-1.13.14}/slcli/webapp_click.py +0 -0
  89. {systemlink_cli-1.13.12 → systemlink_cli-1.13.14}/slcli/workflow_preview.py +0 -0
  90. {systemlink_cli-1.13.12 → systemlink_cli-1.13.14}/slcli/workflows_click.py +0 -0
  91. {systemlink_cli-1.13.12 → systemlink_cli-1.13.14}/slcli/workitem_click.py +0 -0
  92. {systemlink_cli-1.13.12 → systemlink_cli-1.13.14}/slcli/workspace_click.py +0 -0
  93. {systemlink_cli-1.13.12 → systemlink_cli-1.13.14}/slcli/workspace_utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: systemlink-cli
3
- Version: 1.13.12
3
+ Version: 1.13.14
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.13.12"
3
+ version = "1.13.14"
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.13.12"
4
+ __version__ = "1.13.14"
@@ -219,9 +219,12 @@ def _warn_if_large_dataset(
219
219
  product_substitutions: List[Any],
220
220
  order_by: Optional[str] = None,
221
221
  descending: bool = False,
222
- ) -> None:
222
+ ) -> Optional[int]:
223
223
  """Check dataset size and warn user if fetching large number of items.
224
224
 
225
+ Returns the total count of matching items, or None if the check failed.
226
+ Callers can use a zero return value to short-circuit expensive queries.
227
+
225
228
  Args:
226
229
  endpoint: API endpoint ("query-products" or "query-results").
227
230
  filter_expr: Optional Dynamic LINQ filter expression.
@@ -230,6 +233,9 @@ def _warn_if_large_dataset(
230
233
  product_substitutions: Product filter substitutions (for results only).
231
234
  order_by: Field to order by (included in count check for consistency).
232
235
  descending: Whether results should be in descending order.
236
+
237
+ Returns:
238
+ Total count of matching items, or None if the probe request failed.
233
239
  """
234
240
  url = f"{_get_testmonitor_base_url()}/{endpoint}"
235
241
  payload: Dict[str, Any] = {
@@ -266,9 +272,10 @@ def _warn_if_large_dataset(
266
272
  f"ℹ️ Fetching {total_count} items...",
267
273
  err=True,
268
274
  )
275
+ return total_count
269
276
  except Exception:
270
277
  # If count check fails, proceed without warning
271
- pass
278
+ return None
272
279
 
273
280
 
274
281
  def _query_all_products(
@@ -1132,8 +1139,9 @@ def register_testmonitor_commands(cli: Any) -> None:
1132
1139
  )
1133
1140
  click.echo(json.dumps(summary_stats, indent=2))
1134
1141
  else:
1135
- # Check total count first to warn about large datasets
1136
- _warn_if_large_dataset(
1142
+ # Probe total count: warns for large datasets, short-circuits
1143
+ # when zero results match (avoids the full paginated query).
1144
+ total = _warn_if_large_dataset(
1137
1145
  endpoint="query-results",
1138
1146
  filter_expr=filter_expr,
1139
1147
  substitutions=merged_subs,
@@ -1142,15 +1150,19 @@ def register_testmonitor_commands(cli: Any) -> None:
1142
1150
  order_by=order_by,
1143
1151
  descending=descending,
1144
1152
  )
1145
- results = _query_all_results(
1146
- filter_expr,
1147
- merged_subs,
1148
- product_filter_expr,
1149
- product_subs,
1150
- order_by,
1151
- descending,
1152
- take=take,
1153
- )
1153
+
1154
+ if total == 0:
1155
+ results: List[Dict[str, Any]] = []
1156
+ else:
1157
+ results = _query_all_results(
1158
+ filter_expr,
1159
+ merged_subs,
1160
+ product_filter_expr,
1161
+ product_subs,
1162
+ order_by,
1163
+ descending,
1164
+ take=take,
1165
+ )
1154
1166
 
1155
1167
  mock_resp: Any = FilteredResponse({"results": results})
1156
1168
  UniversalResponseHandler.handle_list_response(
@@ -65,24 +65,79 @@ def check_readonly_mode(operation: str = "this operation") -> None:
65
65
  sys.exit(ExitCodes.PERMISSION_DENIED)
66
66
 
67
67
 
68
+ def _extract_response_error_message(exc: Exception) -> Optional[str]:
69
+ """Extract a human-readable error message from an HTTP error response body.
70
+
71
+ Attempts to parse the response JSON and extract the error message from
72
+ common SystemLink error formats (e.g., ``{"error": {"message": "..."}}``)
73
+ or a top-level ``"message"`` field.
74
+
75
+ Args:
76
+ exc: The exception, expected to be a ``requests.HTTPError`` with a response.
77
+
78
+ Returns:
79
+ The extracted message string, or ``None`` if unavailable.
80
+ """
81
+ response = getattr(exc, "response", None)
82
+ if response is None:
83
+ return None
84
+ try:
85
+ body = response.json()
86
+ except (ValueError, AttributeError):
87
+ return None
88
+
89
+ # SystemLink standard: {"error": {"message": "..."}}
90
+ error_obj = body.get("error") if isinstance(body, dict) else None
91
+ if isinstance(error_obj, dict):
92
+ msg = error_obj.get("message")
93
+ if msg:
94
+ return str(msg)
95
+
96
+ # Fallback: top-level {"message": "..."}
97
+ if isinstance(body, dict):
98
+ msg = body.get("message")
99
+ if msg:
100
+ return str(msg)
101
+
102
+ return None
103
+
104
+
105
+ def _extract_response_status_code(exc: Exception) -> Optional[int]:
106
+ """Extract the HTTP status code from an exception response, if present."""
107
+ response = getattr(exc, "response", None)
108
+ status_code = getattr(response, "status_code", None)
109
+ return status_code if isinstance(status_code, int) else None
110
+
111
+
68
112
  def handle_api_error(exc: Exception) -> None:
69
113
  """Handle API errors with appropriate exit codes and consistent formatting.
70
114
 
115
+ When the exception originates from an HTTP response that contains a JSON
116
+ error body, the server-provided message is displayed instead of the generic
117
+ HTTP status line so that users get actionable feedback.
118
+
71
119
  Args:
72
120
  exc: The exception to handle
73
121
  """
74
- error_msg = str(exc).lower()
75
- if "not found" in error_msg:
76
- click.echo(f"✗ Resource not found: {exc}", err=True)
122
+ # Try to get a detailed message from the response body first
123
+ detail = _extract_response_error_message(exc)
124
+ display_msg = detail if detail else str(exc)
125
+ status_code = _extract_response_status_code(exc)
126
+
127
+ error_msg = display_msg.lower()
128
+ if status_code == 404 or "not found" in error_msg:
129
+ click.echo(f"✗ Resource not found: {display_msg}", err=True)
77
130
  sys.exit(ExitCodes.NOT_FOUND)
78
- elif "permission" in error_msg or "unauthorized" in error_msg or "forbidden" in error_msg:
79
- click.echo(f"✗ Permission denied: {exc}", err=True)
131
+ elif status_code in (401, 403) or (
132
+ "permission" in error_msg or "unauthorized" in error_msg or "forbidden" in error_msg
133
+ ):
134
+ click.echo(f"✗ Permission denied: {display_msg}", err=True)
80
135
  sys.exit(ExitCodes.PERMISSION_DENIED)
81
136
  elif "network" in error_msg or "connection" in error_msg:
82
- click.echo(f"✗ Network error: {exc}", err=True)
137
+ click.echo(f"✗ Network error: {display_msg}", err=True)
83
138
  sys.exit(ExitCodes.NETWORK_ERROR)
84
139
  else:
85
- click.echo(f"✗ Error: {exc}", err=True)
140
+ click.echo(f"✗ Error: {display_msg}", err=True)
86
141
  sys.exit(ExitCodes.GENERAL_ERROR)
87
142
 
88
143