systemlink-cli 1.11.0__tar.gz → 1.11.2__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.11.0 → systemlink_cli-1.11.2}/PKG-INFO +1 -1
- {systemlink_cli-1.11.0 → systemlink_cli-1.11.2}/pyproject.toml +1 -1
- {systemlink_cli-1.11.0 → systemlink_cli-1.11.2}/slcli/_version.py +1 -1
- {systemlink_cli-1.11.0 → systemlink_cli-1.11.2}/slcli/skills/nipkg-file-package/SKILL.md +79 -33
- {systemlink_cli-1.11.0 → systemlink_cli-1.11.2}/slcli/skills/systemlink-notebook/SKILL.md +76 -3
- {systemlink_cli-1.11.0 → systemlink_cli-1.11.2}/slcli/skills/systemlink-notebook/references/notebook-patterns.md +27 -2
- {systemlink_cli-1.11.0 → systemlink_cli-1.11.2}/LICENSE +0 -0
- {systemlink_cli-1.11.0 → systemlink_cli-1.11.2}/dff-editor/editor.js +0 -0
- {systemlink_cli-1.11.0 → systemlink_cli-1.11.2}/dff-editor/index.html +0 -0
- {systemlink_cli-1.11.0 → systemlink_cli-1.11.2}/slcli/__init__.py +0 -0
- {systemlink_cli-1.11.0 → systemlink_cli-1.11.2}/slcli/__main__.py +0 -0
- {systemlink_cli-1.11.0 → systemlink_cli-1.11.2}/slcli/asset_click.py +0 -0
- {systemlink_cli-1.11.0 → systemlink_cli-1.11.2}/slcli/cli_formatters.py +0 -0
- {systemlink_cli-1.11.0 → systemlink_cli-1.11.2}/slcli/cli_utils.py +0 -0
- {systemlink_cli-1.11.0 → systemlink_cli-1.11.2}/slcli/comment_click.py +0 -0
- {systemlink_cli-1.11.0 → systemlink_cli-1.11.2}/slcli/completion_click.py +0 -0
- {systemlink_cli-1.11.0 → systemlink_cli-1.11.2}/slcli/config.py +0 -0
- {systemlink_cli-1.11.0 → systemlink_cli-1.11.2}/slcli/config_click.py +0 -0
- {systemlink_cli-1.11.0 → systemlink_cli-1.11.2}/slcli/dff_click.py +0 -0
- {systemlink_cli-1.11.0 → systemlink_cli-1.11.2}/slcli/dff_decorators.py +0 -0
- {systemlink_cli-1.11.0 → systemlink_cli-1.11.2}/slcli/example_click.py +0 -0
- {systemlink_cli-1.11.0 → systemlink_cli-1.11.2}/slcli/example_loader.py +0 -0
- {systemlink_cli-1.11.0 → systemlink_cli-1.11.2}/slcli/example_provisioner.py +0 -0
- {systemlink_cli-1.11.0 → systemlink_cli-1.11.2}/slcli/examples/README.md +0 -0
- {systemlink_cli-1.11.0 → systemlink_cli-1.11.2}/slcli/examples/_schema/schema-v1.0.json +0 -0
- {systemlink_cli-1.11.0 → systemlink_cli-1.11.2}/slcli/examples/demo-complete-workflow/README.md +0 -0
- {systemlink_cli-1.11.0 → systemlink_cli-1.11.2}/slcli/examples/demo-complete-workflow/config.yaml +0 -0
- {systemlink_cli-1.11.0 → systemlink_cli-1.11.2}/slcli/examples/demo-test-plans/README.md +0 -0
- {systemlink_cli-1.11.0 → systemlink_cli-1.11.2}/slcli/examples/demo-test-plans/config.yaml +0 -0
- {systemlink_cli-1.11.0 → systemlink_cli-1.11.2}/slcli/examples/exercise-5-1-parametric-insights/README.md +0 -0
- {systemlink_cli-1.11.0 → systemlink_cli-1.11.2}/slcli/examples/exercise-5-1-parametric-insights/config.yaml +0 -0
- {systemlink_cli-1.11.0 → systemlink_cli-1.11.2}/slcli/examples/exercise-7-1-test-plans/README.md +0 -0
- {systemlink_cli-1.11.0 → systemlink_cli-1.11.2}/slcli/examples/exercise-7-1-test-plans/config.yaml +0 -0
- {systemlink_cli-1.11.0 → systemlink_cli-1.11.2}/slcli/examples/spec-compliance-notebooks/README.md +0 -0
- {systemlink_cli-1.11.0 → systemlink_cli-1.11.2}/slcli/examples/spec-compliance-notebooks/config.yaml +0 -0
- {systemlink_cli-1.11.0 → systemlink_cli-1.11.2}/slcli/examples/spec-compliance-notebooks/notebooks/SpecAnalysis_ComplianceCalculation.ipynb +0 -0
- {systemlink_cli-1.11.0 → systemlink_cli-1.11.2}/slcli/examples/spec-compliance-notebooks/notebooks/SpecComplianceCalculation.ipynb +0 -0
- {systemlink_cli-1.11.0 → systemlink_cli-1.11.2}/slcli/examples/spec-compliance-notebooks/notebooks/SpecfileExtractionAndIngestion.ipynb +0 -0
- {systemlink_cli-1.11.0 → systemlink_cli-1.11.2}/slcli/examples/spec-compliance-notebooks/spec_template.xlsx +0 -0
- {systemlink_cli-1.11.0 → systemlink_cli-1.11.2}/slcli/feed_click.py +0 -0
- {systemlink_cli-1.11.0 → systemlink_cli-1.11.2}/slcli/file_click.py +0 -0
- {systemlink_cli-1.11.0 → systemlink_cli-1.11.2}/slcli/function_click.py +0 -0
- {systemlink_cli-1.11.0 → systemlink_cli-1.11.2}/slcli/function_templates.py +0 -0
- {systemlink_cli-1.11.0 → systemlink_cli-1.11.2}/slcli/main.py +0 -0
- {systemlink_cli-1.11.0 → systemlink_cli-1.11.2}/slcli/mcp_click.py +0 -0
- {systemlink_cli-1.11.0 → systemlink_cli-1.11.2}/slcli/mcp_reachability.py +0 -0
- {systemlink_cli-1.11.0 → systemlink_cli-1.11.2}/slcli/mcp_server.py +0 -0
- {systemlink_cli-1.11.0 → systemlink_cli-1.11.2}/slcli/notebook_click.py +0 -0
- {systemlink_cli-1.11.0 → systemlink_cli-1.11.2}/slcli/platform.py +0 -0
- {systemlink_cli-1.11.0 → systemlink_cli-1.11.2}/slcli/policy_click.py +0 -0
- {systemlink_cli-1.11.0 → systemlink_cli-1.11.2}/slcli/policy_utils.py +0 -0
- {systemlink_cli-1.11.0 → systemlink_cli-1.11.2}/slcli/profiles.py +0 -0
- {systemlink_cli-1.11.0 → systemlink_cli-1.11.2}/slcli/response_handlers.py +0 -0
- {systemlink_cli-1.11.0 → systemlink_cli-1.11.2}/slcli/rich_output.py +0 -0
- {systemlink_cli-1.11.0 → systemlink_cli-1.11.2}/slcli/routine_click.py +0 -0
- {systemlink_cli-1.11.0 → systemlink_cli-1.11.2}/slcli/skill_click.py +0 -0
- {systemlink_cli-1.11.0 → systemlink_cli-1.11.2}/slcli/skills/slcli/SKILL.md +0 -0
- {systemlink_cli-1.11.0 → systemlink_cli-1.11.2}/slcli/skills/slcli/references/analysis-recipes.md +0 -0
- {systemlink_cli-1.11.0 → systemlink_cli-1.11.2}/slcli/skills/slcli/references/commands.md +0 -0
- {systemlink_cli-1.11.0 → systemlink_cli-1.11.2}/slcli/skills/slcli/references/datasheet-workflow.md +0 -0
- {systemlink_cli-1.11.0 → systemlink_cli-1.11.2}/slcli/skills/slcli/references/filtering.md +0 -0
- {systemlink_cli-1.11.0 → systemlink_cli-1.11.2}/slcli/skills/slcli/references/troubleshooting.md +0 -0
- {systemlink_cli-1.11.0 → systemlink_cli-1.11.2}/slcli/skills/systemlink-job-debugging/SKILL.md +0 -0
- {systemlink_cli-1.11.0 → systemlink_cli-1.11.2}/slcli/skills/systemlink-notebook/references/interfaces.md +0 -0
- {systemlink_cli-1.11.0 → systemlink_cli-1.11.2}/slcli/skills/systemlink-python-test/SKILL.md +0 -0
- {systemlink_cli-1.11.0 → systemlink_cli-1.11.2}/slcli/skills/systemlink-webapp/SKILL.md +0 -0
- {systemlink_cli-1.11.0 → systemlink_cli-1.11.2}/slcli/skills/systemlink-webapp/references/deployment.md +0 -0
- {systemlink_cli-1.11.0 → systemlink_cli-1.11.2}/slcli/skills/systemlink-webapp/references/layout-patterns.md +0 -0
- {systemlink_cli-1.11.0 → systemlink_cli-1.11.2}/slcli/skills/systemlink-webapp/references/nimble-angular.md +0 -0
- {systemlink_cli-1.11.0 → systemlink_cli-1.11.2}/slcli/skills/systemlink-webapp/references/systemlink-services.md +0 -0
- {systemlink_cli-1.11.0 → systemlink_cli-1.11.2}/slcli/spec_click.py +0 -0
- {systemlink_cli-1.11.0 → systemlink_cli-1.11.2}/slcli/ssl_trust.py +0 -0
- {systemlink_cli-1.11.0 → systemlink_cli-1.11.2}/slcli/system_click.py +0 -0
- {systemlink_cli-1.11.0 → systemlink_cli-1.11.2}/slcli/system_query_utils.py +0 -0
- {systemlink_cli-1.11.0 → systemlink_cli-1.11.2}/slcli/table_utils.py +0 -0
- {systemlink_cli-1.11.0 → systemlink_cli-1.11.2}/slcli/tag_click.py +0 -0
- {systemlink_cli-1.11.0 → systemlink_cli-1.11.2}/slcli/templates_click.py +0 -0
- {systemlink_cli-1.11.0 → systemlink_cli-1.11.2}/slcli/testmonitor_click.py +0 -0
- {systemlink_cli-1.11.0 → systemlink_cli-1.11.2}/slcli/universal_handlers.py +0 -0
- {systemlink_cli-1.11.0 → systemlink_cli-1.11.2}/slcli/user_click.py +0 -0
- {systemlink_cli-1.11.0 → systemlink_cli-1.11.2}/slcli/utils.py +0 -0
- {systemlink_cli-1.11.0 → systemlink_cli-1.11.2}/slcli/web_editor.py +0 -0
- {systemlink_cli-1.11.0 → systemlink_cli-1.11.2}/slcli/webapp_click.py +0 -0
- {systemlink_cli-1.11.0 → systemlink_cli-1.11.2}/slcli/workflow_preview.py +0 -0
- {systemlink_cli-1.11.0 → systemlink_cli-1.11.2}/slcli/workflows_click.py +0 -0
- {systemlink_cli-1.11.0 → systemlink_cli-1.11.2}/slcli/workitem_click.py +0 -0
- {systemlink_cli-1.11.0 → systemlink_cli-1.11.2}/slcli/workspace_click.py +0 -0
- {systemlink_cli-1.11.0 → systemlink_cli-1.11.2}/slcli/workspace_utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "systemlink-cli"
|
|
3
|
-
version = "1.11.
|
|
3
|
+
version = "1.11.2"
|
|
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" }]
|
|
@@ -224,23 +224,25 @@ dependencies, create a Salt state file (`deploy/install.sls`) and apply it throu
|
|
|
224
224
|
SystemLink Systems Manager. A typical SLS for a Python test package covers:
|
|
225
225
|
|
|
226
226
|
1. **Download and install Python** — use the official Windows installer with `/quiet`,
|
|
227
|
-
`InstallAllUsers=1`, `PrependPath=1`.
|
|
227
|
+
`InstallAllUsers=1`, `PrependPath=1`. For `TargetDir`, pass the full
|
|
228
|
+
`key=value` as one quoted argument when using `Program Files`:
|
|
228
229
|
```yaml
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
230
|
+
install-python:
|
|
231
|
+
cmd.run:
|
|
232
|
+
- name: >-
|
|
233
|
+
"C:\Windows\Temp\python-3.12.9-amd64.exe"
|
|
234
|
+
/quiet
|
|
235
|
+
InstallAllUsers=1
|
|
236
|
+
PrependPath=1
|
|
237
|
+
"TargetDir=C:\Program Files\Python312"
|
|
238
|
+
Include_launcher=1
|
|
239
|
+
- shell: cmd
|
|
240
|
+
- unless: >-
|
|
241
|
+
powershell -Command "$r64 = Get-ChildItem 'HKLM:\SOFTWARE\Python\PythonCore' -ErrorAction SilentlyContinue; if ($r64) { exit 0 }; $r32 = Get-ChildItem 'HKLM:\SOFTWARE\WOW6432Node\Python\PythonCore' -ErrorAction SilentlyContinue; if ($r32) { exit 0 }; if (Test-Path 'C:\Program Files\Python312\python.exe') { exit 0 }; exit 1"
|
|
239
242
|
```
|
|
240
|
-
**Critical**:
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
the value portion (`TargetDir="C:\Program Files\Python312"`).
|
|
243
|
+
**Critical**: In SystemLink/Salt `cmd.run` contexts, keep `TargetDir` as a single
|
|
244
|
+
quoted argument (for example `"TargetDir=C:\Program Files\Python312"`) and verify
|
|
245
|
+
install path checks.
|
|
244
246
|
|
|
245
247
|
2. **Add Python to PATH** — `win_path.exists` for both the install dir and `Scripts\`.
|
|
246
248
|
|
|
@@ -326,26 +328,69 @@ SystemLink Systems Manager. A typical SLS for a Python test package covers:
|
|
|
326
328
|
|
|
327
329
|
## Auto-Versioning Build Script Pattern
|
|
328
330
|
|
|
329
|
-
|
|
330
|
-
|
|
331
|
+
Use an incrementing build counter stored in `package/build_number.txt` alongside a base
|
|
332
|
+
version in `package/version.txt`. This produces short, valid Debian version strings
|
|
333
|
+
(`major.minor.patch.build`) that NI Package Manager and SystemLink accept reliably.
|
|
334
|
+
|
|
335
|
+
**Why not timestamps?** A timestamp-based suffix like `1.0.0.20260420083348` exceeds the
|
|
336
|
+
length that SystemLink's `pkg.installed` Salt state can resolve correctly. The feed lookup
|
|
337
|
+
fails silently and the state reports the package as not found. Always use a short numeric
|
|
338
|
+
build counter instead.
|
|
339
|
+
|
|
340
|
+
### File layout
|
|
341
|
+
|
|
342
|
+
```text
|
|
343
|
+
package/
|
|
344
|
+
├── version.txt # base version, e.g. "1.0.1" (edit when bumping major/minor/patch)
|
|
345
|
+
├── build_number.txt # next build number to use, e.g. "0" (auto-incremented by build script)
|
|
346
|
+
├── control
|
|
347
|
+
├── instructions
|
|
348
|
+
├── postinstall.bat
|
|
349
|
+
└── preuninstall.bat
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
If your build script stamps versions through `control.template`, keep
|
|
353
|
+
`control.template` in source control as the input template and write the stamped
|
|
354
|
+
result to `control` during the build.
|
|
355
|
+
|
|
356
|
+
### Build script snippet
|
|
331
357
|
|
|
332
358
|
```bat
|
|
333
|
-
|
|
334
|
-
|
|
359
|
+
set SCRIPT_DIR=%~dp0
|
|
360
|
+
set CONTROL_DIR=%SCRIPT_DIR%build\nipkg\control
|
|
361
|
+
set DEPLOY_SLS=%SCRIPT_DIR%deploy\install.sls
|
|
362
|
+
set VERSION_FILE=%SCRIPT_DIR%package\version.txt
|
|
363
|
+
set BUILD_NUMBER_FILE=%SCRIPT_DIR%package\build_number.txt
|
|
364
|
+
set BASE_VERSION=
|
|
365
|
+
set NEXT_BUILD=
|
|
366
|
+
|
|
367
|
+
set /p BASE_VERSION=<"%VERSION_FILE%"
|
|
368
|
+
set /p NEXT_BUILD=<"%BUILD_NUMBER_FILE%"
|
|
369
|
+
if "%BASE_VERSION%"=="" (
|
|
370
|
+
echo Failed to read package\version.txt.
|
|
371
|
+
exit /b 1
|
|
372
|
+
)
|
|
373
|
+
if "%NEXT_BUILD%"=="" set NEXT_BUILD=0
|
|
374
|
+
set PACKAGE_VERSION=%BASE_VERSION%.%NEXT_BUILD%
|
|
375
|
+
set /a WRITE_BUILD=%NEXT_BUILD%+1
|
|
376
|
+
echo %WRITE_BUILD%>"%BUILD_NUMBER_FILE%"
|
|
377
|
+
echo Version for this build: %PACKAGE_VERSION%
|
|
335
378
|
|
|
336
379
|
REM Stamp version into control file
|
|
337
380
|
powershell -NoProfile -Command ^
|
|
338
|
-
"(Get-Content -Raw
|
|
339
|
-
| Set-Content -Encoding ASCII '%CONTROL_DIR%\control'"
|
|
381
|
+
"$p='%CONTROL_DIR%\control.template'; $o='%CONTROL_DIR%\control'; (Get-Content -Raw $p) -replace '(?m)^Version:\s*.*$','Version: %PACKAGE_VERSION%' | Set-Content -Encoding ASCII $o"
|
|
340
382
|
|
|
341
383
|
REM Stamp version into deploy\install.sls (keeps SLS in sync with feed)
|
|
342
384
|
powershell -NoProfile -Command ^
|
|
343
|
-
"(Get-Content -Raw
|
|
344
|
-
| Set-Content -Encoding ASCII '%DEPLOY_SLS%'"
|
|
385
|
+
"$p='%DEPLOY_SLS%'; (Get-Content -Raw $p) -replace '(?m)^\s*-\s*my-package:\s*.*$',' - my-package: %PACKAGE_VERSION%' | Set-Content -Encoding ASCII $p"
|
|
345
386
|
```
|
|
346
387
|
|
|
347
|
-
|
|
348
|
-
|
|
388
|
+
### Workflow
|
|
389
|
+
|
|
390
|
+
1. On the first build: `version.txt` = `1.0.1`, `build_number.txt` = `0` → produces `1.0.1.0`, writes `1` back.
|
|
391
|
+
2. On the next build: reads `1`, produces `1.0.1.1`, writes `2` back, and so on.
|
|
392
|
+
3. To bump major/minor/patch: edit `version.txt` and reset `build_number.txt` to `0`.
|
|
393
|
+
4. Commit both files so the counter is shared across machines / CI runs.
|
|
349
394
|
|
|
350
395
|
## Complete Working SLS Example
|
|
351
396
|
|
|
@@ -370,7 +415,7 @@ install-python:
|
|
|
370
415
|
/quiet
|
|
371
416
|
InstallAllUsers=1
|
|
372
417
|
PrependPath=1
|
|
373
|
-
TargetDir=C:\
|
|
418
|
+
"TargetDir=C:\Program Files\Python312"
|
|
374
419
|
Include_launcher=1
|
|
375
420
|
- shell: cmd
|
|
376
421
|
- unless: >-
|
|
@@ -407,7 +452,7 @@ install-my-test-package:
|
|
|
407
452
|
pkg.installed:
|
|
408
453
|
- install_recommends: true
|
|
409
454
|
- pkgs:
|
|
410
|
-
- my-package: 1.0.0
|
|
455
|
+
- my-package: 1.0.1.0
|
|
411
456
|
- require:
|
|
412
457
|
- module: add-my-test-feed
|
|
413
458
|
|
|
@@ -443,9 +488,9 @@ install-pip-deps:
|
|
|
443
488
|
```
|
|
444
489
|
|
|
445
490
|
**Key rules from testing:**
|
|
446
|
-
- `TargetDir`
|
|
447
|
-
|
|
448
|
-
|
|
491
|
+
- Pass `TargetDir` as one quoted key/value argument when using `Program Files`
|
|
492
|
+
(`"TargetDir=C:\Program Files\Python312"`) and keep explicit registry/path checks
|
|
493
|
+
in `unless` guards.
|
|
449
494
|
- `unless` guards for Python install must check the registry AND the file path; using
|
|
450
495
|
only `python --version` can match the Salt minion's bundled Python, not the system one.
|
|
451
496
|
- The `require` type for `pkg.installed` after `module.run` is `module:`, not `pkgrepo:`.
|
|
@@ -464,7 +509,7 @@ file server-side and rejects anything that is not parseable as YAML.
|
|
|
464
509
|
supported**. Hardcode all values (Python version, paths, URLs) directly.
|
|
465
510
|
- **Validate locally** before uploading: `python -c "import yaml; yaml.safe_load(open('install.sls'))"`
|
|
466
511
|
- **Salt state functions that work**: `cmd.run`, `file.managed`, `file.serialize`,
|
|
467
|
-
`file.absent`, `win_path.exists`, `system.reboot`, `
|
|
512
|
+
`file.absent`, `win_path.exists`, `system.reboot`, `module.run`, `pkg.installed`
|
|
468
513
|
— any valid Salt state module is accepted as long as the YAML parses.
|
|
469
514
|
|
|
470
515
|
### Uploading States to SystemLink
|
|
@@ -474,8 +519,9 @@ The SystemLink Systems State API (`/nisystemsstate/v1/`) provides two ways to cr
|
|
|
474
519
|
#### Option 1: JSON API — Package/Feed States Only
|
|
475
520
|
|
|
476
521
|
`POST /nisystemsstate/v1/states` with a JSON body. This only supports `packages` and
|
|
477
|
-
`feeds` arrays
|
|
478
|
-
|
|
522
|
+
`feeds` arrays for package/feed-only workflows. Use this when the state only needs to
|
|
523
|
+
install nipkg packages. For custom SLS uploads, continue using the verified
|
|
524
|
+
`module.run` + `pkg.mod_repo` pattern.
|
|
479
525
|
|
|
480
526
|
```python
|
|
481
527
|
state = {
|
|
@@ -71,7 +71,8 @@ in the cell's `.metadata` field for SystemLink to recognize the parameters and o
|
|
|
71
71
|
|
|
72
72
|
- `papermill.parameters` keys **must** match the variable names in the code cell
|
|
73
73
|
- `systemlink.parameters[].id` **must** match the variable names in the code cell
|
|
74
|
-
- `systemlink.outputs[].id` **must** match the
|
|
74
|
+
- `systemlink.outputs[].id` **must** match the `id` field in each output object in the
|
|
75
|
+
`result` list passed to `sb.glue('result', result)`
|
|
75
76
|
- `tags: ["parameters"]` is **required** — this is how papermill identifies the cell
|
|
76
77
|
- `systemlink.version` must be `2` for most notebooks, or `1` for **Work Item Automations**
|
|
77
78
|
- Supported parameter types: `string`, `integer`, `float`, `boolean`, `string[]`
|
|
@@ -140,6 +141,21 @@ result = [{
|
|
|
140
141
|
sb.glue('result', result)
|
|
141
142
|
```
|
|
142
143
|
|
|
144
|
+
### Output validation guardrails
|
|
145
|
+
|
|
146
|
+
- Ensure `result` is a list of output objects, not a dict.
|
|
147
|
+
- Ensure each output object has: `display_name`, `id`, `type`, and `data`.
|
|
148
|
+
- Ensure `type` is one of the supported output types and matches metadata.
|
|
149
|
+
- Ensure output `id` exactly matches `systemlink.outputs[].id`.
|
|
150
|
+
- For `data_frame`, ensure `data.columns` and `data.values` are present and aligned.
|
|
151
|
+
|
|
152
|
+
For Systems Grid notebooks, validate that:
|
|
153
|
+
|
|
154
|
+
- The first column is exactly `minion id`.
|
|
155
|
+
- The first value in each row is the system ID string.
|
|
156
|
+
- For single-column grid reports, use exactly two columns: `minion id` and the report value column.
|
|
157
|
+
- If the notebook is intended to populate one grid column, avoid extra columns unless explicitly required.
|
|
158
|
+
|
|
143
159
|
### Systems Grid reports
|
|
144
160
|
|
|
145
161
|
When the notebook is used as a Systems Grid column, the `data_frame` output
|
|
@@ -152,6 +168,26 @@ df_dict = {
|
|
|
152
168
|
}
|
|
153
169
|
```
|
|
154
170
|
|
|
171
|
+
Common Systems Grid pattern for one metric per system:
|
|
172
|
+
|
|
173
|
+
```python
|
|
174
|
+
metric_by_system = {item['id']: item['metric_value'] for item in data.data}
|
|
175
|
+
df = pd.DataFrame.from_dict(metric_by_system, orient='index', columns=['metric'])
|
|
176
|
+
|
|
177
|
+
df_dict = {
|
|
178
|
+
'columns': ['minion id', 'metric'],
|
|
179
|
+
'values': df.reset_index().values.tolist()
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
result = [{
|
|
183
|
+
'display_name': 'Metric',
|
|
184
|
+
'id': 'metric_output',
|
|
185
|
+
'type': 'data_frame',
|
|
186
|
+
'data': df_dict
|
|
187
|
+
}]
|
|
188
|
+
sb.glue('result', result)
|
|
189
|
+
```
|
|
190
|
+
|
|
155
191
|
## Common Imports
|
|
156
192
|
|
|
157
193
|
```python
|
|
@@ -172,7 +208,6 @@ from nisystemlink.clients.assetmanagement import AssetManagementClient
|
|
|
172
208
|
|
|
173
209
|
# Direct HTTP (when no typed client exists)
|
|
174
210
|
import requests
|
|
175
|
-
from nisystemlink.clients.core import HttpConfigurationManager
|
|
176
211
|
config = HttpConfigurationManager.get_configuration()
|
|
177
212
|
base_url = config.server_uri.rstrip("/")
|
|
178
213
|
headers = {"x-ni-api-key": config.api_keys[0]}
|
|
@@ -191,7 +226,6 @@ headers = {"x-ni-api-key": config.api_keys[0]}
|
|
|
191
226
|
|
|
192
227
|
When using direct HTTP, prefer the OpenAPI docs first to confirm the service base
|
|
193
228
|
path, request body shape, and response schema before writing notebook code.
|
|
194
|
-
|
|
195
229
|
## Systems Query Pattern
|
|
196
230
|
|
|
197
231
|
The `SystemsApi` uses a projection/filter pattern for querying:
|
|
@@ -210,6 +244,22 @@ query_result = api.get_systems_by_query(query=query)
|
|
|
210
244
|
data = await query_result
|
|
211
245
|
```
|
|
212
246
|
|
|
247
|
+
### Async and response handling guardrails
|
|
248
|
+
|
|
249
|
+
- In notebook cells, prefer top-level `await` over `asyncio.run(...)`.
|
|
250
|
+
- Do not call `asyncio.run(...)` inside notebook execution cells; kernels may already have an active event loop.
|
|
251
|
+
- `get_systems_by_query` can return typed response objects (for example `QuerySystemsResponse`). Prefer `data.data` (or `getattr(data, 'data', ...)`) over assuming a plain dict/list.
|
|
252
|
+
|
|
253
|
+
Safe extraction pattern:
|
|
254
|
+
|
|
255
|
+
```python
|
|
256
|
+
query_result = api.get_systems_by_query(query=query)
|
|
257
|
+
data = await query_result
|
|
258
|
+
systems = getattr(data, 'data', None)
|
|
259
|
+
if systems is None:
|
|
260
|
+
systems = data.get('data', data) if isinstance(data, dict) else data
|
|
261
|
+
```
|
|
262
|
+
|
|
213
263
|
### Common filter expressions
|
|
214
264
|
|
|
215
265
|
| Filter | Description |
|
|
@@ -318,6 +368,29 @@ the server rejects the update.
|
|
|
318
368
|
reading the `Workspace` field from the active profile. Do not assume "Default" or
|
|
319
369
|
prompt the user unless `slcli info` shows no workspace configured.
|
|
320
370
|
|
|
371
|
+
## Execution Validation Checklist
|
|
372
|
+
|
|
373
|
+
After create/update, always validate with a fresh (non-cached) execution before concluding success:
|
|
374
|
+
|
|
375
|
+
```bash
|
|
376
|
+
slcli notebook execute sync -n <NOTEBOOK_ID> -w "<WORKSPACE_NAME>" --no-cache -f json
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
Validation steps:
|
|
380
|
+
|
|
381
|
+
1. Confirm `status` is `SUCCEEDED` and `errorCode` is `NO_ERROR`.
|
|
382
|
+
2. Confirm output `id` and `type` match parameters-cell metadata.
|
|
383
|
+
3. Confirm `data.columns` and `data.values` shape matches the target interface.
|
|
384
|
+
4. If output still looks old, check `cachedResult`; rerun with `--no-cache`.
|
|
385
|
+
|
|
386
|
+
## Known Failure Modes and Fixes
|
|
387
|
+
|
|
388
|
+
- `ValueError: No kernel name found in notebook`: restore notebook-level `metadata.kernelspec`.
|
|
389
|
+
- Notebook output not shown in SystemLink UI: restore parameters-cell `systemlink` metadata and `tags: ["parameters"]`.
|
|
390
|
+
- Systems Grid format mismatch: ensure first column is `minion id` and output is a valid `data_frame` payload.
|
|
391
|
+
- Runtime loop errors (`asyncio.run() cannot be called from a running event loop`): replace `asyncio.run(...)` with top-level `await`.
|
|
392
|
+
- Result appears unchanged after update: rerun with `--no-cache` and re-check returned execution JSON.
|
|
393
|
+
|
|
321
394
|
## Example
|
|
322
395
|
|
|
323
396
|
See [notebook-patterns.md](./references/notebook-patterns.md) for a complete
|
|
@@ -22,6 +22,7 @@ Import Python modules for executing the notebook.
|
|
|
22
22
|
|
|
23
23
|
**Cell 2 — Imports (code)**
|
|
24
24
|
```python
|
|
25
|
+
import re
|
|
25
26
|
import pandas as pd
|
|
26
27
|
import scrapbook as sb
|
|
27
28
|
|
|
@@ -99,8 +100,32 @@ The cell metadata must include:
|
|
|
99
100
|
```python
|
|
100
101
|
api = SystemsApi()
|
|
101
102
|
|
|
102
|
-
|
|
103
|
-
|
|
103
|
+
def _escape_filter_value(value: str) -> str:
|
|
104
|
+
"""Escape backslashes first, then quotes, for safe quoted filter literals."""
|
|
105
|
+
return value.replace("\\", "\\\\").replace('"', '\\"')
|
|
106
|
+
|
|
107
|
+
def _validate_package_name(value: str) -> str:
|
|
108
|
+
normalized = value.strip()
|
|
109
|
+
if not re.fullmatch(r"[A-Za-z0-9](?:[A-Za-z0-9._-]*[A-Za-z0-9])?", normalized):
|
|
110
|
+
raise ValueError("package must only contain letters, numbers, dot, dash, or underscore")
|
|
111
|
+
return normalized
|
|
112
|
+
|
|
113
|
+
def _validate_filter_fragment(value: str) -> str:
|
|
114
|
+
normalized = value.strip()
|
|
115
|
+
if not normalized:
|
|
116
|
+
return "!string.IsNullOrEmpty(id)"
|
|
117
|
+
if not re.fullmatch(r'[A-Za-z0-9_ ."\-()\[\],:&|=!<>]*', normalized):
|
|
118
|
+
raise ValueError("systems_filter contains unsupported characters")
|
|
119
|
+
return normalized
|
|
120
|
+
|
|
121
|
+
safe_package = _escape_filter_value(_validate_package_name(package))
|
|
122
|
+
base_filter = _validate_filter_fragment(systems_filter)
|
|
123
|
+
|
|
124
|
+
projection = (
|
|
125
|
+
f'new(id, packages.data["{safe_package}"].displayversion, '
|
|
126
|
+
f'packages.data["{safe_package}"].version)'
|
|
127
|
+
)
|
|
128
|
+
filter = base_filter + f' && packages.data.keys.Contains("{safe_package}")'
|
|
104
129
|
|
|
105
130
|
query_sys_request = QuerySystemsRequest(skip=0, projection=projection, filter=filter)
|
|
106
131
|
query_result = api.get_systems_by_query(query=query_sys_request)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{systemlink_cli-1.11.0 → systemlink_cli-1.11.2}/slcli/examples/demo-complete-workflow/README.md
RENAMED
|
File without changes
|
{systemlink_cli-1.11.0 → systemlink_cli-1.11.2}/slcli/examples/demo-complete-workflow/config.yaml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{systemlink_cli-1.11.0 → systemlink_cli-1.11.2}/slcli/examples/exercise-7-1-test-plans/README.md
RENAMED
|
File without changes
|
{systemlink_cli-1.11.0 → systemlink_cli-1.11.2}/slcli/examples/exercise-7-1-test-plans/config.yaml
RENAMED
|
File without changes
|
{systemlink_cli-1.11.0 → systemlink_cli-1.11.2}/slcli/examples/spec-compliance-notebooks/README.md
RENAMED
|
File without changes
|
{systemlink_cli-1.11.0 → systemlink_cli-1.11.2}/slcli/examples/spec-compliance-notebooks/config.yaml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{systemlink_cli-1.11.0 → systemlink_cli-1.11.2}/slcli/skills/slcli/references/analysis-recipes.md
RENAMED
|
File without changes
|
|
File without changes
|
{systemlink_cli-1.11.0 → systemlink_cli-1.11.2}/slcli/skills/slcli/references/datasheet-workflow.md
RENAMED
|
File without changes
|
|
File without changes
|
{systemlink_cli-1.11.0 → systemlink_cli-1.11.2}/slcli/skills/slcli/references/troubleshooting.md
RENAMED
|
File without changes
|
{systemlink_cli-1.11.0 → systemlink_cli-1.11.2}/slcli/skills/systemlink-job-debugging/SKILL.md
RENAMED
|
File without changes
|
|
File without changes
|
{systemlink_cli-1.11.0 → systemlink_cli-1.11.2}/slcli/skills/systemlink-python-test/SKILL.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|