systemlink-cli 1.9.4__tar.gz → 1.11.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.
- {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/PKG-INFO +1 -1
- {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/pyproject.toml +1 -1
- {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/_version.py +1 -1
- {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/main.py +7 -0
- {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/mcp_reachability.py +11 -0
- {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/mcp_server.py +44 -5
- {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/platform.py +104 -5
- systemlink_cli-1.11.0/slcli/skills/slcli/SKILL.md +180 -0
- systemlink_cli-1.9.4/slcli/skills/slcli/SKILL.md → systemlink_cli-1.11.0/slcli/skills/slcli/references/commands.md +54 -125
- systemlink_cli-1.11.0/slcli/skills/slcli/references/datasheet-workflow.md +510 -0
- systemlink_cli-1.11.0/slcli/skills/slcli/references/troubleshooting.md +45 -0
- systemlink_cli-1.11.0/slcli/spec_click.py +2141 -0
- {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/system_click.py +424 -65
- systemlink_cli-1.11.0/slcli/system_query_utils.py +161 -0
- {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/LICENSE +0 -0
- {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/dff-editor/editor.js +0 -0
- {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/dff-editor/index.html +0 -0
- {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/__init__.py +0 -0
- {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/__main__.py +0 -0
- {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/asset_click.py +0 -0
- {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/cli_formatters.py +0 -0
- {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/cli_utils.py +0 -0
- {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/comment_click.py +0 -0
- {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/completion_click.py +0 -0
- {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/config.py +0 -0
- {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/config_click.py +0 -0
- {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/dff_click.py +0 -0
- {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/dff_decorators.py +0 -0
- {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/example_click.py +0 -0
- {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/example_loader.py +0 -0
- {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/example_provisioner.py +0 -0
- {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/examples/README.md +0 -0
- {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/examples/_schema/schema-v1.0.json +0 -0
- {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/examples/demo-complete-workflow/README.md +0 -0
- {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/examples/demo-complete-workflow/config.yaml +0 -0
- {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/examples/demo-test-plans/README.md +0 -0
- {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/examples/demo-test-plans/config.yaml +0 -0
- {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/examples/exercise-5-1-parametric-insights/README.md +0 -0
- {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/examples/exercise-5-1-parametric-insights/config.yaml +0 -0
- {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/examples/exercise-7-1-test-plans/README.md +0 -0
- {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/examples/exercise-7-1-test-plans/config.yaml +0 -0
- {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/examples/spec-compliance-notebooks/README.md +0 -0
- {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/examples/spec-compliance-notebooks/config.yaml +0 -0
- {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/examples/spec-compliance-notebooks/notebooks/SpecAnalysis_ComplianceCalculation.ipynb +0 -0
- {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/examples/spec-compliance-notebooks/notebooks/SpecComplianceCalculation.ipynb +0 -0
- {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/examples/spec-compliance-notebooks/notebooks/SpecfileExtractionAndIngestion.ipynb +0 -0
- {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/examples/spec-compliance-notebooks/spec_template.xlsx +0 -0
- {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/feed_click.py +0 -0
- {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/file_click.py +0 -0
- {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/function_click.py +0 -0
- {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/function_templates.py +0 -0
- {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/mcp_click.py +0 -0
- {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/notebook_click.py +0 -0
- {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/policy_click.py +0 -0
- {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/policy_utils.py +0 -0
- {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/profiles.py +0 -0
- {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/response_handlers.py +0 -0
- {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/rich_output.py +0 -0
- {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/routine_click.py +0 -0
- {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/skill_click.py +0 -0
- {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/skills/nipkg-file-package/SKILL.md +0 -0
- {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/skills/slcli/references/analysis-recipes.md +0 -0
- {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/skills/slcli/references/filtering.md +0 -0
- {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/skills/systemlink-job-debugging/SKILL.md +0 -0
- {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/skills/systemlink-notebook/SKILL.md +0 -0
- {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/skills/systemlink-notebook/references/interfaces.md +0 -0
- {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/skills/systemlink-notebook/references/notebook-patterns.md +0 -0
- {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/skills/systemlink-python-test/SKILL.md +0 -0
- {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/skills/systemlink-webapp/SKILL.md +0 -0
- {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/skills/systemlink-webapp/references/deployment.md +0 -0
- {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/skills/systemlink-webapp/references/layout-patterns.md +0 -0
- {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/skills/systemlink-webapp/references/nimble-angular.md +0 -0
- {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/skills/systemlink-webapp/references/systemlink-services.md +0 -0
- {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/ssl_trust.py +0 -0
- {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/table_utils.py +0 -0
- {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/tag_click.py +0 -0
- {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/templates_click.py +0 -0
- {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/testmonitor_click.py +0 -0
- {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/universal_handlers.py +0 -0
- {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/user_click.py +0 -0
- {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/utils.py +0 -0
- {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/web_editor.py +0 -0
- {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/webapp_click.py +0 -0
- {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/workflow_preview.py +0 -0
- {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/workflows_click.py +0 -0
- {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/workitem_click.py +0 -0
- {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/workspace_click.py +0 -0
- {systemlink_cli-1.9.4 → systemlink_cli-1.11.0}/slcli/workspace_utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "systemlink-cli"
|
|
3
|
-
version = "1.
|
|
3
|
+
version = "1.11.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" }]
|
|
@@ -30,6 +30,7 @@ from .rich_output import install_rich_output
|
|
|
30
30
|
from .rich_output import render_table
|
|
31
31
|
from .routine_click import register_routine_commands
|
|
32
32
|
from .skill_click import register_skill_commands
|
|
33
|
+
from .spec_click import register_spec_commands
|
|
33
34
|
from .ssl_trust import OS_TRUST_INJECTED, OS_TRUST_REASON
|
|
34
35
|
from .system_click import register_system_commands
|
|
35
36
|
from .tag_click import register_tag_commands
|
|
@@ -365,6 +366,11 @@ def info(format: str, skip_health: bool) -> None:
|
|
|
365
366
|
workspace_display = truncate(workspace)
|
|
366
367
|
info_rows.append(["Workspace", workspace_display])
|
|
367
368
|
|
|
369
|
+
system_query_endpoint = platform_info.get("system_query_endpoint")
|
|
370
|
+
if system_query_endpoint:
|
|
371
|
+
system_query_display = truncate(str(system_query_endpoint))
|
|
372
|
+
info_rows.append(["System Query", system_query_display])
|
|
373
|
+
|
|
368
374
|
file_query_endpoint = platform_info.get("file_query_endpoint")
|
|
369
375
|
if file_query_endpoint:
|
|
370
376
|
if (
|
|
@@ -427,6 +433,7 @@ register_notebook_commands(cli)
|
|
|
427
433
|
register_policy_commands(cli)
|
|
428
434
|
register_routine_commands(cli)
|
|
429
435
|
register_system_commands(cli)
|
|
436
|
+
register_spec_commands(cli)
|
|
430
437
|
register_tag_commands(cli)
|
|
431
438
|
register_testmonitor_commands(cli)
|
|
432
439
|
register_webapp_commands(cli)
|
|
@@ -35,6 +35,17 @@ def is_reachability_failure(exc: BaseException) -> bool:
|
|
|
35
35
|
return True
|
|
36
36
|
|
|
37
37
|
message = str(candidate).lower()
|
|
38
|
+
if "/mcp" in message and any(
|
|
39
|
+
token in message
|
|
40
|
+
for token in (
|
|
41
|
+
"server error",
|
|
42
|
+
"not found",
|
|
43
|
+
"method not allowed",
|
|
44
|
+
"bad request",
|
|
45
|
+
)
|
|
46
|
+
):
|
|
47
|
+
return True
|
|
48
|
+
|
|
38
49
|
if any(
|
|
39
50
|
token in message
|
|
40
51
|
for token in (
|
|
@@ -271,7 +271,41 @@ def query_systems(
|
|
|
271
271
|
take: int = 100,
|
|
272
272
|
) -> str:
|
|
273
273
|
"""Query systems by alias, connection state, workspace, or raw filter."""
|
|
274
|
-
|
|
274
|
+
import requests as requests_lib
|
|
275
|
+
|
|
276
|
+
from .system_query_utils import (
|
|
277
|
+
MATERIALIZED_SYSTEM_MCP_PROJECTION,
|
|
278
|
+
build_materialized_system_search_filter,
|
|
279
|
+
get_system_query_url,
|
|
280
|
+
get_system_search_url,
|
|
281
|
+
)
|
|
282
|
+
from .utils import make_api_request
|
|
283
|
+
|
|
284
|
+
materialized_filter = None
|
|
285
|
+
if filter is None:
|
|
286
|
+
materialized_filter = build_materialized_system_search_filter(
|
|
287
|
+
alias=alias,
|
|
288
|
+
state=state,
|
|
289
|
+
workspace_id=workspace,
|
|
290
|
+
)
|
|
291
|
+
|
|
292
|
+
if filter is None:
|
|
293
|
+
search_payload: Dict[str, Any] = {
|
|
294
|
+
"take": take,
|
|
295
|
+
"projection": MATERIALIZED_SYSTEM_MCP_PROJECTION,
|
|
296
|
+
}
|
|
297
|
+
if materialized_filter:
|
|
298
|
+
search_payload["filter"] = materialized_filter
|
|
299
|
+
try:
|
|
300
|
+
data = make_api_request(
|
|
301
|
+
"POST",
|
|
302
|
+
get_system_search_url(),
|
|
303
|
+
payload=search_payload,
|
|
304
|
+
handle_errors=False,
|
|
305
|
+
).json()
|
|
306
|
+
return _dump(_normalize_systems(data))
|
|
307
|
+
except requests_lib.RequestException:
|
|
308
|
+
pass
|
|
275
309
|
|
|
276
310
|
filter_parts: List[str] = []
|
|
277
311
|
if alias:
|
|
@@ -283,11 +317,16 @@ def query_systems(
|
|
|
283
317
|
if filter:
|
|
284
318
|
filter_parts.append(filter)
|
|
285
319
|
|
|
286
|
-
|
|
320
|
+
query_payload: Dict[str, Any] = {"take": take}
|
|
287
321
|
if filter_parts:
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
data =
|
|
322
|
+
query_payload["filter"] = " and ".join(filter_parts)
|
|
323
|
+
|
|
324
|
+
data = make_api_request(
|
|
325
|
+
"POST",
|
|
326
|
+
get_system_query_url(),
|
|
327
|
+
payload=query_payload,
|
|
328
|
+
handle_errors=False,
|
|
329
|
+
).json()
|
|
291
330
|
return _dump(_normalize_systems(data))
|
|
292
331
|
|
|
293
332
|
|
|
@@ -59,6 +59,8 @@ FEATURE_DISPLAY_NAMES: Dict[str, str] = {
|
|
|
59
59
|
FILE_SEARCH_PATH = "/nifile/v1/service-groups/Default/search-files"
|
|
60
60
|
FILE_QUERY_PATH = "/nifile/v1/service-groups/Default/query-files"
|
|
61
61
|
FILE_QUERY_LINQ_PATH = "/nifile/v1/service-groups/Default/query-files-linq"
|
|
62
|
+
SYSTEM_SEARCH_PATH = "/nisysmgmt/v1/materialized/search-systems"
|
|
63
|
+
SYSTEM_QUERY_PATH = "/nisysmgmt/v1/query-systems"
|
|
62
64
|
|
|
63
65
|
|
|
64
66
|
def _get_keyring_config() -> Dict[str, Any]:
|
|
@@ -195,7 +197,7 @@ SERVICE_CHECKS: List[List[str]] = [
|
|
|
195
197
|
["Auth", "GET", "/niauth/v1/policies"],
|
|
196
198
|
["Test Monitor", "GET", "/nitestmonitor/v2/results?take=0"],
|
|
197
199
|
["Asset Management", "POST", "/niapm/v1/query-assets"],
|
|
198
|
-
["Systems", "POST",
|
|
200
|
+
["Systems", "POST", SYSTEM_QUERY_PATH],
|
|
199
201
|
["Tag", "GET", "/nitag/v2/tags?take=0"],
|
|
200
202
|
["File", "POST", FILE_SEARCH_PATH],
|
|
201
203
|
["Notebook", "POST", "/ninotebook/v1/notebook/query"],
|
|
@@ -319,6 +321,87 @@ def get_file_query_capability(api_url: str, api_key: str) -> Dict[str, Any]:
|
|
|
319
321
|
}
|
|
320
322
|
|
|
321
323
|
|
|
324
|
+
def get_system_query_capability(api_url: str, api_key: str) -> Dict[str, Any]:
|
|
325
|
+
"""Determine which systems query endpoint is available for this server."""
|
|
326
|
+
headers = {
|
|
327
|
+
"x-ni-api-key": api_key,
|
|
328
|
+
"Content-Type": "application/json",
|
|
329
|
+
"User-Agent": "SystemLink-CLI/1.0 (cross-platform)",
|
|
330
|
+
}
|
|
331
|
+
ssl_verify = get_ssl_verify()
|
|
332
|
+
|
|
333
|
+
try:
|
|
334
|
+
search_resp = requests.post(
|
|
335
|
+
f"{api_url}{SYSTEM_SEARCH_PATH}",
|
|
336
|
+
headers=headers,
|
|
337
|
+
json={"take": 1, "projection": ["id"]},
|
|
338
|
+
verify=ssl_verify,
|
|
339
|
+
timeout=10,
|
|
340
|
+
)
|
|
341
|
+
if search_resp.status_code in (200, 400):
|
|
342
|
+
return {
|
|
343
|
+
"status": "ok",
|
|
344
|
+
"system_query_endpoint": "search-systems",
|
|
345
|
+
"materialized_search_available": True,
|
|
346
|
+
}
|
|
347
|
+
if search_resp.status_code in (401, 403):
|
|
348
|
+
return {
|
|
349
|
+
"status": "unauthorized",
|
|
350
|
+
"system_query_endpoint": "search-systems",
|
|
351
|
+
"materialized_search_available": True,
|
|
352
|
+
}
|
|
353
|
+
if search_resp.status_code not in (404, 501):
|
|
354
|
+
return {
|
|
355
|
+
"status": "error" if search_resp.status_code >= 500 else "not_found",
|
|
356
|
+
"system_query_endpoint": None,
|
|
357
|
+
"materialized_search_available": True,
|
|
358
|
+
}
|
|
359
|
+
except requests.RequestException:
|
|
360
|
+
return {
|
|
361
|
+
"status": "unreachable",
|
|
362
|
+
"system_query_endpoint": None,
|
|
363
|
+
"materialized_search_available": None,
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
try:
|
|
367
|
+
query_resp = requests.post(
|
|
368
|
+
f"{api_url}{SYSTEM_QUERY_PATH}",
|
|
369
|
+
headers=headers,
|
|
370
|
+
json={"take": 1, "projection": "new(id)"},
|
|
371
|
+
verify=ssl_verify,
|
|
372
|
+
timeout=10,
|
|
373
|
+
)
|
|
374
|
+
if query_resp.status_code in (200, 400):
|
|
375
|
+
return {
|
|
376
|
+
"status": "ok",
|
|
377
|
+
"system_query_endpoint": "query-systems",
|
|
378
|
+
"materialized_search_available": False,
|
|
379
|
+
}
|
|
380
|
+
if query_resp.status_code in (401, 403):
|
|
381
|
+
return {
|
|
382
|
+
"status": "unauthorized",
|
|
383
|
+
"system_query_endpoint": "query-systems",
|
|
384
|
+
"materialized_search_available": False,
|
|
385
|
+
}
|
|
386
|
+
if query_resp.status_code in (404, 501):
|
|
387
|
+
return {
|
|
388
|
+
"status": "not_found",
|
|
389
|
+
"system_query_endpoint": None,
|
|
390
|
+
"materialized_search_available": False,
|
|
391
|
+
}
|
|
392
|
+
return {
|
|
393
|
+
"status": "error",
|
|
394
|
+
"system_query_endpoint": None,
|
|
395
|
+
"materialized_search_available": False,
|
|
396
|
+
}
|
|
397
|
+
except requests.RequestException:
|
|
398
|
+
return {
|
|
399
|
+
"status": "unreachable",
|
|
400
|
+
"system_query_endpoint": None,
|
|
401
|
+
"materialized_search_available": False,
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
|
|
322
405
|
def check_service_status(api_url: str, api_key: str) -> Dict[str, Any]:
|
|
323
406
|
"""Probe key SystemLink services and report their status.
|
|
324
407
|
|
|
@@ -334,10 +417,12 @@ def check_service_status(api_url: str, api_key: str) -> Dict[str, Any]:
|
|
|
334
417
|
- auth_valid: bool | None - whether the API key is authorized (None if unreachable)
|
|
335
418
|
- services: dict mapping service name to status string
|
|
336
419
|
("ok", "unauthorized", "not_found", "error", "unreachable")
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
420
|
+
- file_query_endpoint: selected file query endpoint, if available
|
|
421
|
+
- elasticsearch_available: bool | None - whether search-files is available
|
|
422
|
+
- system_query_endpoint: selected systems query endpoint, if available
|
|
423
|
+
- materialized_search_available: bool | None - whether search-systems is available
|
|
424
|
+
- platform: detected platform string (PLATFORM_SLE, PLATFORM_SLS,
|
|
425
|
+
PLATFORM_UNREACHABLE, PLATFORM_UNKNOWN)
|
|
341
426
|
"""
|
|
342
427
|
headers = {
|
|
343
428
|
"x-ni-api-key": api_key,
|
|
@@ -396,6 +481,8 @@ def check_service_status(api_url: str, api_key: str) -> Dict[str, Any]:
|
|
|
396
481
|
"services": services,
|
|
397
482
|
"file_query_endpoint": None,
|
|
398
483
|
"elasticsearch_available": None,
|
|
484
|
+
"system_query_endpoint": None,
|
|
485
|
+
"materialized_search_available": None,
|
|
399
486
|
"platform": PLATFORM_UNREACHABLE,
|
|
400
487
|
}
|
|
401
488
|
|
|
@@ -416,6 +503,8 @@ def check_service_status(api_url: str, api_key: str) -> Dict[str, Any]:
|
|
|
416
503
|
|
|
417
504
|
file_capability = get_file_query_capability(api_url, api_key)
|
|
418
505
|
services["File"] = file_capability["status"]
|
|
506
|
+
system_capability = get_system_query_capability(api_url, api_key)
|
|
507
|
+
services["Systems"] = system_capability["status"]
|
|
419
508
|
|
|
420
509
|
return {
|
|
421
510
|
"server_reachable": True,
|
|
@@ -423,6 +512,8 @@ def check_service_status(api_url: str, api_key: str) -> Dict[str, Any]:
|
|
|
423
512
|
"services": services,
|
|
424
513
|
"file_query_endpoint": file_capability["file_query_endpoint"],
|
|
425
514
|
"elasticsearch_available": file_capability["elasticsearch_available"],
|
|
515
|
+
"system_query_endpoint": system_capability["system_query_endpoint"],
|
|
516
|
+
"materialized_search_available": system_capability["materialized_search_available"],
|
|
426
517
|
"platform": platform,
|
|
427
518
|
}
|
|
428
519
|
|
|
@@ -473,6 +564,8 @@ def get_platform_info(skip_health: bool = False) -> Dict[str, Any]:
|
|
|
473
564
|
platform = stored_platform
|
|
474
565
|
file_query_endpoint: Optional[str] = None
|
|
475
566
|
elasticsearch_available: Optional[bool] = None
|
|
567
|
+
system_query_endpoint: Optional[str] = None
|
|
568
|
+
materialized_search_available: Optional[bool] = None
|
|
476
569
|
|
|
477
570
|
if not skip_health and logged_in and isinstance(api_url, str) and api_url != "Not configured":
|
|
478
571
|
status = check_service_status(api_url, api_key)
|
|
@@ -482,6 +575,8 @@ def get_platform_info(skip_health: bool = False) -> Dict[str, Any]:
|
|
|
482
575
|
platform = status["platform"]
|
|
483
576
|
file_query_endpoint = status.get("file_query_endpoint")
|
|
484
577
|
elasticsearch_available = status.get("elasticsearch_available")
|
|
578
|
+
system_query_endpoint = status.get("system_query_endpoint")
|
|
579
|
+
materialized_search_available = status.get("materialized_search_available")
|
|
485
580
|
|
|
486
581
|
info: Dict[str, Any] = {
|
|
487
582
|
"api_url": api_url,
|
|
@@ -497,6 +592,10 @@ def get_platform_info(skip_health: bool = False) -> Dict[str, Any]:
|
|
|
497
592
|
info["file_query_endpoint"] = file_query_endpoint
|
|
498
593
|
if elasticsearch_available is not None:
|
|
499
594
|
info["elasticsearch_available"] = elasticsearch_available
|
|
595
|
+
if system_query_endpoint is not None:
|
|
596
|
+
info["system_query_endpoint"] = system_query_endpoint
|
|
597
|
+
if materialized_search_available is not None:
|
|
598
|
+
info["materialized_search_available"] = materialized_search_available
|
|
500
599
|
|
|
501
600
|
if services is not None:
|
|
502
601
|
info["services"] = services
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: slcli
|
|
3
|
+
description: >-
|
|
4
|
+
Query and manage NI SystemLink resources using the slcli command-line interface.
|
|
5
|
+
Covers test results, assets, systems, tags, feeds, files, notebooks,
|
|
6
|
+
routines, work items, work item templates, workflows, test plan templates,
|
|
7
|
+
specifications, products, datasheet-to-specification ingestion (PDF and CSV),
|
|
8
|
+
custom fields, web applications, authorization policies, users, workspaces, and more.
|
|
9
|
+
Use when the user asks about test data analysis, asset management, calibration status,
|
|
10
|
+
system fleet health, operator performance, failure analysis, production metrics,
|
|
11
|
+
equipment utilization, work order tracking, specification management,
|
|
12
|
+
importing datasheets, creating products from spec sheets,
|
|
13
|
+
or any SystemLink resource operations.
|
|
14
|
+
Supports filtering, aggregation, summary statistics, and JSON output for programmatic processing.
|
|
15
|
+
argument-hint: >-
|
|
16
|
+
Describe what you want to do: query test results, import a datasheet,
|
|
17
|
+
list assets, manage work items, etc.
|
|
18
|
+
compatibility: >-
|
|
19
|
+
Requires slcli installed and authenticated (slcli login). Python 3.10+.
|
|
20
|
+
Requires network access to a SystemLink server instance.
|
|
21
|
+
metadata:
|
|
22
|
+
author: ni-kismet
|
|
23
|
+
version: "2.0"
|
|
24
|
+
# --system SYSTEM_ID Assign a system (by minion/system ID). Repeatable.
|
|
25
|
+
# --fixture ASSET_ID Assign a fixture/slot (by asset ID, asset type FIXTURE). Repeatable.
|
|
26
|
+
# --dut ASSET_ID Assign a DUT (by asset ID, asset type DEVICE_UNDER_TEST). Repeatable.
|
|
27
|
+
# Use `slcli asset list --asset-type FIXTURE` to find fixture IDs.
|
|
28
|
+
# Use `slcli system list` to find system IDs.
|
|
29
|
+
# At least one option must be provided; time and resource options can be combined freely.
|
|
30
|
+
slcli workitem schedule <WORK_ITEM_ID> \
|
|
31
|
+
[--start ISO8601] [--end ISO8601] [--duration SECONDS] \
|
|
32
|
+
[--assigned-to USER_ID] \
|
|
33
|
+
[--system SYSTEM_ID]... [--fixture ASSET_ID]... [--dut ASSET_ID]...
|
|
34
|
+
|
|
35
|
+
# Work item template subgroup
|
|
36
|
+
slcli workitem template list [-w WORKSPACE] [--filter TEXT] [-t INT] [-f json]
|
|
37
|
+
slcli workitem template get <TEMPLATE_ID> [-f json]
|
|
38
|
+
slcli workitem template create --name TEXT --type TEXT --template-group TEXT [-w WORKSPACE] [OPTIONS]
|
|
39
|
+
slcli workitem template update <TEMPLATE_ID> [--name TEXT] [--description TEXT] [--summary TEXT]
|
|
40
|
+
slcli workitem template delete <TEMPLATE_ID>... [--yes]
|
|
41
|
+
|
|
42
|
+
# Workflow subgroup
|
|
43
|
+
slcli workitem workflow list [-w WORKSPACE] [-t INT] [-f json]
|
|
44
|
+
slcli workitem workflow get [--id WORKFLOW_ID] [--name NAME] [-f json]
|
|
45
|
+
slcli workitem workflow init [--name TEXT] [--directory DIR] # Scaffold a local workflow file
|
|
46
|
+
slcli workitem workflow create --file PATH [-w WORKSPACE] # Create from JSON file
|
|
47
|
+
slcli workitem workflow import --file PATH [-w WORKSPACE] # Import workflow from JSON
|
|
48
|
+
slcli workitem workflow export [--id WORKFLOW_ID] [--name NAME] [-o FILE] # Export to JSON
|
|
49
|
+
slcli workitem workflow update --id WORKFLOW_ID --file PATH # Update from JSON file
|
|
50
|
+
slcli workitem workflow delete --id WORKFLOW_ID [--yes]
|
|
51
|
+
slcli workitem workflow preview [--file PATH] [--id WORKFLOW_ID] [--html] [--no-open] [-o FILE]
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
**Create work item options:**
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
slcli workitem create \
|
|
58
|
+
--name "Battery Cycle Test" \
|
|
59
|
+
--type testplan \
|
|
60
|
+
--state NEW \
|
|
61
|
+
--part-number "P-BAT-001" \
|
|
62
|
+
--description "Battery capacity test" \
|
|
63
|
+
--assigned-to <user-id> \
|
|
64
|
+
--workflow-id <workflow-id> \
|
|
65
|
+
--workspace Default \
|
|
66
|
+
--format json
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### workflow — Workflow management
|
|
70
|
+
|
|
71
|
+
> **Note:** The standalone `slcli workflow` command group has been replaced by
|
|
72
|
+
> `slcli workitem workflow`. Use `slcli workitem workflow` for all workflow operations.
|
|
73
|
+
> See the **workitem** section above.
|
|
74
|
+
|
|
75
|
+
### webapp — Web application management
|
|
76
|
+
|
|
77
|
+
Scaffold, package, and publish custom web applications to SystemLink.
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
slcli webapp init <DIRECTORY> # Scaffold the Angular starter
|
|
81
|
+
slcli webapp manifest init <DIRECTORY> [OPTIONS] # Create nipkg.config.json for packaging
|
|
82
|
+
slcli webapp pack [FOLDER] [--config FILE] [-o OUTPUT_FILE] # Package a webapp into a .nipkg
|
|
83
|
+
slcli webapp list [-w WORKSPACE] [-t INT] [-f json]
|
|
84
|
+
slcli webapp get <WEBAPP_ID> [-f json]
|
|
85
|
+
slcli webapp publish PATH [--workspace NAME] # Upload and publish a webapp
|
|
86
|
+
slcli webapp delete <WEBAPP_ID>
|
|
87
|
+
slcli webapp open <WEBAPP_ID> # Open webapp URL in browser
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
`webapp init` creates the SystemLink Angular starter, not a generic HTML app. The starter installs
|
|
91
|
+
project-scoped skills into `.agents/skills/` and creates `PROMPTS.md` plus `START_HERE.md` so an
|
|
92
|
+
AI assistant can bootstrap the Angular workspace in place with the same Nimble/SystemLink
|
|
93
|
+
conventions described by the `systemlink-webapp` skill.
|
|
94
|
+
|
|
95
|
+
`webapp manifest init` writes `nipkg.config.json` using the Plugin Manager field names
|
|
96
|
+
(`section`, `maintainer`, `homepage`, `xbPlugin`, `slPluginManagerTags`,
|
|
97
|
+
`slPluginManagerMinServerVersion`, `iconFile`). `webapp pack --config ...` consumes that
|
|
98
|
+
metadata, carries the icon into the package, writes the matching control-file fields into the
|
|
99
|
+
generated `.nipkg`, and emits a thin `manifest.json` with `schemaVersion`, `nipkgFile`,
|
|
100
|
+
`sha256`, and any configured provenance fields.
|
|
101
|
+
|
|
102
|
+
### skill — AI skill installation
|
|
103
|
+
|
|
104
|
+
Install bundled skills for supported AI clients.
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
slcli skill install --skill [slcli|systemlink-webapp|systemlink-notebook|all] --client [agents|claude|all] --scope [personal|project|both]
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Client paths:
|
|
111
|
+
|
|
112
|
+
- `agents` — personal: `~/.agents/skills/`, project: `.agents/skills/` (most agents)
|
|
113
|
+
- `claude` — personal: `~/.claude/skills/`, project: `.claude/skills/`
|
|
114
|
+
- `all` — install to both the `agents` and `claude` locations for the selected scope
|
|
115
|
+
|
|
116
|
+
Notes:
|
|
117
|
+
|
|
118
|
+
- `agents` is the default client in interactive mode.
|
|
119
|
+
- `webapp init` installs project-scoped skills into `.agents/skills/` by default.
|
|
120
|
+
|
|
121
|
+
### example — Built-in example resource provisioning
|
|
122
|
+
|
|
123
|
+
Install pre-built demo configurations (systems, assets, DUTs, templates, etc.)
|
|
124
|
+
for training, testing, or evaluation.
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
slcli example list [-f json] # List available examples
|
|
128
|
+
slcli example info <EXAMPLE_ID> # Show example details
|
|
129
|
+
slcli example install <EXAMPLE_ID> [--workspace NAME] # Provision example resources
|
|
130
|
+
slcli example delete <EXAMPLE_ID> [--workspace NAME] # Remove provisioned resources
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## Reference docs
|
|
134
|
+
|
|
135
|
+
Consult these for detailed guidance. Load only what you need for the current task.
|
|
136
|
+
|
|
137
|
+
| Topic | File | When to load |
|
|
138
|
+
| --------------------------- | ----------------------------------------------------------- | ------------------------------------------------------------- |
|
|
139
|
+
| CLI command reference | [commands.md](./references/commands.md) | Looking up command syntax, options, or examples |
|
|
140
|
+
| Datasheet-to-specs workflow | [datasheet-workflow.md](./references/datasheet-workflow.md) | Importing specs from PDF, CSV, or structured text |
|
|
141
|
+
| Filtering guide | [filtering.md](./references/filtering.md) | Advanced LINQ expressions, parameterized queries |
|
|
142
|
+
| Analysis recipes | [analysis-recipes.md](./references/analysis-recipes.md) | Multi-step analysis: yield, calibration, operator performance |
|
|
143
|
+
| Troubleshooting | [troubleshooting.md](./references/troubleshooting.md) | Workspace ID issues, SSL errors, encoding, PowerShell quoting |
|
|
144
|
+
|
|
145
|
+
## Command groups at a glance
|
|
146
|
+
|
|
147
|
+
| Group | Purpose | Key subcommands |
|
|
148
|
+
| ------------- | ----------------------- | ---------------------------------------------------- |
|
|
149
|
+
| `testmonitor` | Test results & products | `result list/get`, `product list/create/update` |
|
|
150
|
+
| `spec` | Specifications | `list`, `query`, `get`, `create`, `import`, `export` |
|
|
151
|
+
| `asset` | Assets & calibration | `list`, `get`, `summary`, `calibration` |
|
|
152
|
+
| `system` | System fleet | `list`, `get`, `compare`, `summary`, `job` |
|
|
153
|
+
| `tag` | Tag read/write | `list`, `get-value`, `set-value`, `create` |
|
|
154
|
+
| `routine` | Event-action routines | `list`, `create`, `enable/disable` (v1 + v2) |
|
|
155
|
+
| `comment` | Resource comments | `list`, `add`, `update`, `delete` |
|
|
156
|
+
| `workitem` | Work items & workflows | `list`, `create`, `schedule`, `template`, `workflow` |
|
|
157
|
+
| `file` | File management | `list`, `upload`, `download`, `query`, `watch` |
|
|
158
|
+
| `notebook` | Jupyter notebooks | `manage list/create`, `execute start/sync` |
|
|
159
|
+
| `feed` | Package feeds | `list`, `create`, `package upload` |
|
|
160
|
+
| `customfield` | Dynamic form fields | `list`, `create`, `export`, `edit` |
|
|
161
|
+
| `template` | Test plan templates | `list`, `import`, `export` |
|
|
162
|
+
| `webapp` | Web applications | `init`, `pack`, `publish`, `list` |
|
|
163
|
+
| `config` | Connection profiles | `list`, `use`, `add`, `delete` |
|
|
164
|
+
| `user` | User management | `list`, `get`, `create`, `update` |
|
|
165
|
+
| `auth` | Authorization policies | `policy list/create`, `template list` |
|
|
166
|
+
| `workspace` | Workspaces | `list`, `get` |
|
|
167
|
+
| `skill` | AI skill installation | `install` |
|
|
168
|
+
| `example` | Demo provisioning | `list`, `install`, `delete` |
|
|
169
|
+
## Key rules
|
|
170
|
+
|
|
171
|
+
1. **Always use `-f json`** when piping output to `jq` or doing programmatic analysis.
|
|
172
|
+
2. **Use `--summary --group-by`** for aggregation instead of fetching all records and counting.
|
|
173
|
+
3. **Use convenience filters first** (e.g., `--status FAILED`), fall back to `--filter` for complex queries.
|
|
174
|
+
4. **Parameterize `--filter` queries** — use `--substitution` instead of string interpolation.
|
|
175
|
+
5. **Combine filters** — convenience filters are ANDed together automatically.
|
|
176
|
+
6. **Use `--take`** to control result volume; JSON returns all matching up to `--take`.
|
|
177
|
+
7. **Status enum values**: `PASSED`, `FAILED`, `RUNNING`, `ERRORED`, `TERMINATED`, `TIMEDOUT`, `WAITING`, `SKIPPED`, `CUSTOM`.
|
|
178
|
+
8. **Exit codes**: 0 = success, 1 = general error, 2 = invalid input, 3 = not found, 4 = permission denied, 5 = network error.
|
|
179
|
+
9. **Prefer workspace IDs** (UUIDs) over names in scripted workflows — some endpoints reject names.
|
|
180
|
+
10. **Use `make_api_request`** from `slcli.utils` for helper scripts — handles auth, SSL, and errors.
|