systemlink-cli 1.27.2__tar.gz → 1.27.3__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.27.2 → systemlink_cli-1.27.3}/PKG-INFO +1 -1
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/pyproject.toml +1 -1
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/_version.py +1 -1
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/main.py +12 -2
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/platform.py +21 -5
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/skill_click.py +107 -4
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/skills/slcli/SKILL.md +28 -11
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/skills/slcli/references/commands.md +38 -1
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/skills/slcli/references/troubleshooting.md +22 -0
- systemlink_cli-1.27.3/slcli/version_click.py +199 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/webapp_click.py +8 -2
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/LICENSE +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/dff-editor/editor.js +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/dff-editor/index.html +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/__init__.py +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/__main__.py +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/alarm_click.py +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/asset_click.py +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/cli_formatters.py +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/cli_utils.py +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/comment_click.py +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/completion_click.py +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/config.py +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/config_click.py +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/dataframe_click.py +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/dff_click.py +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/dff_decorators.py +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/example_click.py +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/example_loader.py +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/example_provisioner.py +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/examples/README.md +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/examples/_schema/schema-v1.0.json +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/examples/demo-data-1/README.md +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/examples/demo-data-1/config.yaml +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/examples/demo-data-2/README.md +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/examples/demo-data-2/config.yaml +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/examples/demo-data-3/config.yaml +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/examples/demo-data-3/leakage-current-results.json +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/examples/demo-data-3/overvoltage-results.json +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/examples/demo-data-3/product-xyz-compliance-report.json +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/examples/demo-data-3/product-xyz-specification.csv +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/examples/demo-data-3/thermal-response-results.json +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/examples/exercise-5-1-parametric-insights/README.md +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/examples/exercise-5-1-parametric-insights/config.yaml +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/examples/exercise-7-1-test-plans/README.md +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/examples/exercise-7-1-test-plans/config.yaml +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/examples/spec-compliance-notebooks/README.md +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/examples/spec-compliance-notebooks/config.yaml +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/examples/spec-compliance-notebooks/notebooks/SpecAnalysis_ComplianceCalculation.ipynb +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/examples/spec-compliance-notebooks/notebooks/SpecComplianceCalculation.ipynb +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/examples/spec-compliance-notebooks/notebooks/SpecfileExtractionAndIngestion.ipynb +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/examples/spec-compliance-notebooks/spec_template.xlsx +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/feed_click.py +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/file_click.py +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/function_click.py +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/function_templates.py +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/mcp_click.py +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/mcp_reachability.py +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/mcp_server.py +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/notebook_click.py +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/pkce.py +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/policy_click.py +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/policy_utils.py +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/profiles.py +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/response_handlers.py +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/rich_output.py +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/routine_click.py +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/skills/slcli/references/analysis-recipes.md +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/skills/slcli/references/datasheet-workflow.md +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/skills/slcli/references/example-authoring.md +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/skills/slcli/references/filtering.md +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/skills/slcli/references/import-specs.min.json +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/skills/slcli/references/job-debugging/overview.md +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/skills/slcli/references/nipkg/overview.md +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/skills/slcli/references/notebook/interfaces.md +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/skills/slcli/references/notebook/notebook-patterns.md +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/skills/slcli/references/notebook/overview.md +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/skills/slcli/references/python-test/overview.md +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/skills/slcli/references/routine-examples.md +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/skills/slcli/references/webapp/angular-ui-packages.md +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/skills/slcli/references/webapp/deployment.md +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/skills/slcli/references/webapp/layout-patterns.md +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/skills/slcli/references/webapp/nimble-angular.md +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/skills/slcli/references/webapp/overview.md +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/skills/slcli/references/webapp/systemlink-services.md +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/skills/slcli/references/webapp/troubleshooting.md +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/skills/slcli/scripts/spec_import_helper.py +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/spec_click.py +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/ssl_trust.py +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/state_click.py +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/system_click.py +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/system_query_utils.py +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/systemlink-logo.svg +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/table_utils.py +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/tag_click.py +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/templates_click.py +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/testmonitor_click.py +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/universal_handlers.py +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/user_click.py +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/utils.py +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/web_editor.py +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/webapp_bootstrap.py +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/webapp_templates/angular/blank/.gitignore +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/webapp_templates/angular/blank/README.md +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/webapp_templates/angular/blank/angular.json +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/webapp_templates/angular/blank/package.json +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/webapp_templates/angular/blank/scripts/filter-sbom.js +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/webapp_templates/angular/blank/src/app/app-routing.module.ts +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/webapp_templates/angular/blank/src/app/app.component.html +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/webapp_templates/angular/blank/src/app/app.component.scss +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/webapp_templates/angular/blank/src/app/app.component.ts +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/webapp_templates/angular/blank/src/app/app.module.ts +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/webapp_templates/angular/blank/src/app/core/layout/app-shell.component.html +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/webapp_templates/angular/blank/src/app/core/layout/app-shell.component.scss +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/webapp_templates/angular/blank/src/app/core/layout/app-shell.component.ts +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/webapp_templates/angular/blank/src/app/core/state/app-view-state.service.ts +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/webapp_templates/angular/blank/src/app/core/systemlink/systemlink-context.service.ts +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/webapp_templates/angular/blank/src/app/core/systemlink/theme-sync.service.ts +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/webapp_templates/angular/blank/src/app/core/systemlink/webapp-home-data.service.ts +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/webapp_templates/angular/blank/src/app/features/assets/assets-page.component.html +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/webapp_templates/angular/blank/src/app/features/assets/assets-page.component.scss +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/webapp_templates/angular/blank/src/app/features/assets/assets-page.component.ts +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/webapp_templates/angular/blank/src/app/features/datasets/datasets-page.component.html +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/webapp_templates/angular/blank/src/app/features/datasets/datasets-page.component.scss +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/webapp_templates/angular/blank/src/app/features/datasets/datasets-page.component.ts +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/webapp_templates/angular/blank/src/app/features/home/home-page.component.html +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/webapp_templates/angular/blank/src/app/features/home/home-page.component.scss +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/webapp_templates/angular/blank/src/app/features/home/home-page.component.ts +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/webapp_templates/angular/blank/src/app/features/master-detail/master-detail-page.component.html +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/webapp_templates/angular/blank/src/app/features/master-detail/master-detail-page.component.scss +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/webapp_templates/angular/blank/src/app/features/master-detail/master-detail-page.component.ts +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/webapp_templates/angular/blank/src/app/features/operations/operations-page.component.html +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/webapp_templates/angular/blank/src/app/features/operations/operations-page.component.scss +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/webapp_templates/angular/blank/src/app/features/operations/operations-page.component.ts +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/webapp_templates/angular/blank/src/app/features/settings/settings-page.component.html +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/webapp_templates/angular/blank/src/app/features/settings/settings-page.component.scss +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/webapp_templates/angular/blank/src/app/features/settings/settings-page.component.ts +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/webapp_templates/angular/blank/src/app/shared/components/empty-state.component.html +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/webapp_templates/angular/blank/src/app/shared/components/empty-state.component.scss +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/webapp_templates/angular/blank/src/app/shared/components/empty-state.component.ts +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/webapp_templates/angular/blank/src/app/shared/components/error-banner.component.html +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/webapp_templates/angular/blank/src/app/shared/components/error-banner.component.scss +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/webapp_templates/angular/blank/src/app/shared/components/error-banner.component.ts +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/webapp_templates/angular/blank/src/app/shared/components/loading-state.component.html +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/webapp_templates/angular/blank/src/app/shared/components/loading-state.component.scss +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/webapp_templates/angular/blank/src/app/shared/components/loading-state.component.ts +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/webapp_templates/angular/blank/src/app/shared/states/view-state.model.ts +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/webapp_templates/angular/blank/src/assets/README.md +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/webapp_templates/angular/blank/src/index.html +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/webapp_templates/angular/blank/src/main.ts +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/webapp_templates/angular/blank/src/styles.scss +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/webapp_templates/angular/blank/tsconfig.app.json +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/webapp_templates/angular/blank/tsconfig.json +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/workflow_preview.py +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/workflows_click.py +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/workitem_click.py +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/workspace_click.py +0 -0
- {systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/workspace_utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "systemlink-cli"
|
|
3
|
-
version = "1.27.
|
|
3
|
+
version = "1.27.3"
|
|
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" }]
|
|
@@ -43,6 +43,7 @@ from .templates_click import register_templates_commands
|
|
|
43
43
|
from .testmonitor_click import register_testmonitor_commands
|
|
44
44
|
from .user_click import register_user_commands
|
|
45
45
|
from .utils import describe_config_source
|
|
46
|
+
from .version_click import register_version_commands
|
|
46
47
|
from .webapp_click import register_webapp_commands
|
|
47
48
|
from .workitem_click import register_workitem_commands
|
|
48
49
|
from .workspace_click import register_workspace_commands
|
|
@@ -71,7 +72,15 @@ def _configure_rich_click_command_groups() -> None:
|
|
|
71
72
|
"slcli": [
|
|
72
73
|
{
|
|
73
74
|
"name": "Configure",
|
|
74
|
-
"commands": [
|
|
75
|
+
"commands": [
|
|
76
|
+
"config",
|
|
77
|
+
"login",
|
|
78
|
+
"logout",
|
|
79
|
+
"info",
|
|
80
|
+
"version",
|
|
81
|
+
"completion",
|
|
82
|
+
"example",
|
|
83
|
+
],
|
|
75
84
|
},
|
|
76
85
|
{
|
|
77
86
|
"name": "Administer",
|
|
@@ -363,7 +372,7 @@ def cli(ctx: base_click.Context, version: bool, profile: Optional[str]) -> None:
|
|
|
363
372
|
|
|
364
373
|
# Check for mandatory migration BEFORE any command runs
|
|
365
374
|
# Skip migration check only for version flag and config migrate command
|
|
366
|
-
if ctx.invoked_subcommand not in (None, "config"):
|
|
375
|
+
if ctx.invoked_subcommand not in (None, "config", "version"):
|
|
367
376
|
from .profiles import ProfileConfig, has_keyring_credentials, migrate_from_keyring
|
|
368
377
|
|
|
369
378
|
config_path = ProfileConfig.get_config_path()
|
|
@@ -769,5 +778,6 @@ register_testmonitor_commands(cli)
|
|
|
769
778
|
register_webapp_commands(cli)
|
|
770
779
|
register_skill_commands(cli)
|
|
771
780
|
register_user_commands(cli)
|
|
781
|
+
register_version_commands(cli)
|
|
772
782
|
register_workitem_commands(cli)
|
|
773
783
|
register_workspace_commands(cli)
|
|
@@ -168,8 +168,9 @@ def get_platform() -> str:
|
|
|
168
168
|
|
|
169
169
|
Detection priority:
|
|
170
170
|
1. SYSTEMLINK_PLATFORM environment variable (explicit, most reliable)
|
|
171
|
-
2.
|
|
172
|
-
3.
|
|
171
|
+
2. Platform stored on the active profile (set during login via endpoint probing)
|
|
172
|
+
3. Stored platform from keyring config (legacy fallback)
|
|
173
|
+
4. Return PLATFORM_UNKNOWN if no explicit or stored platform is available
|
|
173
174
|
|
|
174
175
|
Note: Results are cached for performance. Use clear_platform_cache() to reset.
|
|
175
176
|
|
|
@@ -182,11 +183,26 @@ def get_platform() -> str:
|
|
|
182
183
|
if env_platform in (PLATFORM_SLE, PLATFORM_SLS):
|
|
183
184
|
return env_platform
|
|
184
185
|
|
|
185
|
-
|
|
186
|
-
|
|
186
|
+
if not any(os.environ.get(name) for name in ("SLCLI_API_URL", "SYSTEMLINK_API_URL")):
|
|
187
|
+
try:
|
|
188
|
+
from .profiles import get_active_profile
|
|
189
|
+
|
|
190
|
+
profile = get_active_profile()
|
|
191
|
+
profile_platform = str(profile.platform or "").upper() if profile else ""
|
|
192
|
+
if profile_platform in (PLATFORM_SLE, PLATFORM_SLS):
|
|
193
|
+
return profile_platform
|
|
194
|
+
except (
|
|
195
|
+
FileNotFoundError,
|
|
196
|
+
json.JSONDecodeError,
|
|
197
|
+
KeyError,
|
|
198
|
+
AttributeError,
|
|
199
|
+
click.ClickException,
|
|
200
|
+
):
|
|
201
|
+
pass
|
|
202
|
+
|
|
187
203
|
cfg = _get_keyring_config()
|
|
188
204
|
if cfg:
|
|
189
|
-
platform = cfg.get("platform", "")
|
|
205
|
+
platform = str(cfg.get("platform", "")).upper()
|
|
190
206
|
if platform in (PLATFORM_SLE, PLATFORM_SLS):
|
|
191
207
|
return platform
|
|
192
208
|
|
|
@@ -6,11 +6,14 @@ from pathlib import Path
|
|
|
6
6
|
from typing import Any, Dict, List, Optional, Tuple
|
|
7
7
|
|
|
8
8
|
import click
|
|
9
|
+
from packaging.version import InvalidVersion, Version
|
|
9
10
|
|
|
11
|
+
from ._version import __version__
|
|
10
12
|
from .utils import ExitCodes, format_success
|
|
11
13
|
|
|
12
14
|
SKILL_NAME = "slcli"
|
|
13
15
|
_FALLBACK_SKILL_CHOICES = [SKILL_NAME]
|
|
16
|
+
_VERSION_FILENAME = ".slcli-version"
|
|
14
17
|
|
|
15
18
|
# Mapping of client name -> (personal skills dir, project subdir relative to repo root)
|
|
16
19
|
# personal dir uses Path.home() so it's always resolved at call time via _personal_dir().
|
|
@@ -119,6 +122,23 @@ def _expand_skill_selection(skill_names: Optional[List[str]] = None) -> List[str
|
|
|
119
122
|
return requested
|
|
120
123
|
|
|
121
124
|
|
|
125
|
+
def _skill_installation_status(destination_root: Path, skill_name: str) -> str:
|
|
126
|
+
"""Return the version status of an installed skill."""
|
|
127
|
+
destination_dir = destination_root / skill_name
|
|
128
|
+
if not destination_dir.exists():
|
|
129
|
+
return "missing"
|
|
130
|
+
|
|
131
|
+
version_file = destination_dir / _VERSION_FILENAME
|
|
132
|
+
if not version_file.exists():
|
|
133
|
+
return "unversioned"
|
|
134
|
+
|
|
135
|
+
try:
|
|
136
|
+
installed_version = version_file.read_text(encoding="utf-8").strip()
|
|
137
|
+
return "current" if Version(installed_version) >= Version(__version__) else "outdated"
|
|
138
|
+
except (InvalidVersion, OSError):
|
|
139
|
+
return "unversioned"
|
|
140
|
+
|
|
141
|
+
|
|
122
142
|
def install_skills_to_directory(
|
|
123
143
|
directory: Path,
|
|
124
144
|
skill_names: Optional[List[str]] = None,
|
|
@@ -150,6 +170,7 @@ def install_skills_to_directory(
|
|
|
150
170
|
destination_dir.unlink()
|
|
151
171
|
|
|
152
172
|
shutil.copytree(source_dir, destination_dir)
|
|
173
|
+
(destination_dir / _VERSION_FILENAME).write_text(f"{__version__}\n", encoding="utf-8")
|
|
153
174
|
|
|
154
175
|
return len(selected_skills)
|
|
155
176
|
|
|
@@ -184,6 +205,19 @@ def _resolve_destinations(clients: List[str], scope: str) -> List[Path]:
|
|
|
184
205
|
return seen
|
|
185
206
|
|
|
186
207
|
|
|
208
|
+
def _requested_destinations(
|
|
209
|
+
directory: Optional[Path], client: Optional[str], scope: Optional[str]
|
|
210
|
+
) -> List[Path]:
|
|
211
|
+
"""Resolve either a custom skills directory or client-specific destinations."""
|
|
212
|
+
if directory is not None:
|
|
213
|
+
if client is not None or scope is not None:
|
|
214
|
+
raise ValueError("--directory cannot be combined with --client or --scope")
|
|
215
|
+
return [directory.expanduser()]
|
|
216
|
+
|
|
217
|
+
selected_clients = CLIENT_CHOICES if client == "all" else [client or "agents"]
|
|
218
|
+
return _resolve_destinations(selected_clients, scope or "project")
|
|
219
|
+
|
|
220
|
+
|
|
187
221
|
def register_skill_commands(cli: Any) -> None:
|
|
188
222
|
"""Register the skill command group with the CLI.
|
|
189
223
|
|
|
@@ -217,6 +251,13 @@ def register_skill_commands(cli: Any) -> None:
|
|
|
217
251
|
default=None,
|
|
218
252
|
help="personal (~/ home dirs), project (current repo), or both.",
|
|
219
253
|
)
|
|
254
|
+
@click.option(
|
|
255
|
+
"--directory",
|
|
256
|
+
"-d",
|
|
257
|
+
type=click.Path(file_okay=False, path_type=Path),
|
|
258
|
+
default=None,
|
|
259
|
+
help="Install directly into this skills directory.",
|
|
260
|
+
)
|
|
220
261
|
@click.option(
|
|
221
262
|
"--force",
|
|
222
263
|
"-F",
|
|
@@ -225,7 +266,11 @@ def register_skill_commands(cli: Any) -> None:
|
|
|
225
266
|
help="Overwrite existing skill installation without prompting.",
|
|
226
267
|
)
|
|
227
268
|
def install_skill(
|
|
228
|
-
skill: Optional[str],
|
|
269
|
+
skill: Optional[str],
|
|
270
|
+
client: Optional[str],
|
|
271
|
+
scope: Optional[str],
|
|
272
|
+
directory: Optional[Path],
|
|
273
|
+
force: bool,
|
|
229
274
|
) -> None:
|
|
230
275
|
"""Install bundled skills for supported AI clients."""
|
|
231
276
|
requested_skills: Optional[List[str]]
|
|
@@ -235,11 +280,9 @@ def register_skill_commands(cli: Any) -> None:
|
|
|
235
280
|
assert skill is not None
|
|
236
281
|
requested_skills = [skill.lower()]
|
|
237
282
|
selected_skill_names = _expand_skill_selection(requested_skills)
|
|
238
|
-
selected_clients = CLIENT_CHOICES if client == "all" else [client or "agents"]
|
|
239
|
-
selected_scope = scope or "project"
|
|
240
|
-
destinations = _resolve_destinations(selected_clients, selected_scope)
|
|
241
283
|
|
|
242
284
|
try:
|
|
285
|
+
destinations = _requested_destinations(directory, client, scope)
|
|
243
286
|
installed_count = 0
|
|
244
287
|
for destination in destinations:
|
|
245
288
|
installed_count += install_skills_to_directory(
|
|
@@ -257,6 +300,9 @@ def register_skill_commands(cli: Any) -> None:
|
|
|
257
300
|
except FileNotFoundError as exc:
|
|
258
301
|
click.echo(f"✗ {exc}", err=True)
|
|
259
302
|
sys.exit(ExitCodes.GENERAL_ERROR)
|
|
303
|
+
except OSError as exc:
|
|
304
|
+
click.echo(f"✗ Unable to install skills: {exc}", err=True)
|
|
305
|
+
sys.exit(ExitCodes.GENERAL_ERROR)
|
|
260
306
|
|
|
261
307
|
format_success(
|
|
262
308
|
"Installed bundled AI skills",
|
|
@@ -266,3 +312,60 @@ def register_skill_commands(cli: Any) -> None:
|
|
|
266
312
|
"Installed": str(installed_count),
|
|
267
313
|
},
|
|
268
314
|
)
|
|
315
|
+
|
|
316
|
+
@skill.command(name="check")
|
|
317
|
+
@click.option(
|
|
318
|
+
"--skill",
|
|
319
|
+
"-k",
|
|
320
|
+
type=click.Choice(SKILL_CHOICES + ["all"], case_sensitive=False),
|
|
321
|
+
default=None,
|
|
322
|
+
help=f"Skill to check ({', '.join(SKILL_CHOICES)}, or all).",
|
|
323
|
+
)
|
|
324
|
+
@click.option(
|
|
325
|
+
"--client",
|
|
326
|
+
"-c",
|
|
327
|
+
type=click.Choice(CLIENT_CHOICES + ["all"], case_sensitive=False),
|
|
328
|
+
default=None,
|
|
329
|
+
help="AI client installation to check (agents, claude, or all).",
|
|
330
|
+
)
|
|
331
|
+
@click.option(
|
|
332
|
+
"--scope",
|
|
333
|
+
"-s",
|
|
334
|
+
type=click.Choice(["personal", "project", "both"], case_sensitive=False),
|
|
335
|
+
default=None,
|
|
336
|
+
help="personal (~/ home dirs), project (current repo), or both.",
|
|
337
|
+
)
|
|
338
|
+
@click.option(
|
|
339
|
+
"--directory",
|
|
340
|
+
"-d",
|
|
341
|
+
type=click.Path(file_okay=False, path_type=Path),
|
|
342
|
+
default=None,
|
|
343
|
+
help="Check skills directly in this directory.",
|
|
344
|
+
)
|
|
345
|
+
def check_skills(
|
|
346
|
+
skill: Optional[str],
|
|
347
|
+
client: Optional[str],
|
|
348
|
+
scope: Optional[str],
|
|
349
|
+
directory: Optional[Path],
|
|
350
|
+
) -> None:
|
|
351
|
+
"""Check whether installed skills match the bundled version."""
|
|
352
|
+
requested_skills = None if skill in (None, "all") else [skill.lower()]
|
|
353
|
+
|
|
354
|
+
try:
|
|
355
|
+
selected_skill_names = _expand_skill_selection(requested_skills)
|
|
356
|
+
destinations = _requested_destinations(directory, client, scope)
|
|
357
|
+
except ValueError as exc:
|
|
358
|
+
click.echo(f"✗ {exc}", err=True)
|
|
359
|
+
sys.exit(ExitCodes.INVALID_INPUT)
|
|
360
|
+
|
|
361
|
+
needs_update = False
|
|
362
|
+
click.echo(f"Bundled skill version: {__version__}")
|
|
363
|
+
for destination in destinations:
|
|
364
|
+
for skill_name in selected_skill_names:
|
|
365
|
+
status = _skill_installation_status(destination, skill_name)
|
|
366
|
+
indicator = "✓" if status == "current" else "✗"
|
|
367
|
+
click.echo(f"{indicator} {destination / skill_name}: {status}")
|
|
368
|
+
needs_update = needs_update or status != "current"
|
|
369
|
+
|
|
370
|
+
if needs_update:
|
|
371
|
+
sys.exit(ExitCodes.GENERAL_ERROR)
|
|
@@ -47,17 +47,33 @@ Load only what the current task needs.
|
|
|
47
47
|
|
|
48
48
|
## Default approach
|
|
49
49
|
|
|
50
|
-
1.
|
|
51
|
-
|
|
50
|
+
1. At the start of troubleshooting or a substantial `slcli` workflow, run the
|
|
51
|
+
update preflight once per session:
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
slcli version check
|
|
55
|
+
slcli skill check --client CLIENT --scope SCOPE
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Set `CLIENT` and `SCOPE` to the active skill installation, or use
|
|
59
|
+
`--directory PATH` for a custom skills directory. If the CLI is outdated,
|
|
60
|
+
recommend the update command printed by `version check`. If the skill is
|
|
61
|
+
missing, unversioned, or outdated, recommend `slcli skill install` with the
|
|
62
|
+
same target and `--force`. Ask before running either update because they
|
|
63
|
+
modify the user's environment. A failed or unavailable check is advisory:
|
|
64
|
+
report it briefly and continue the requested workflow unless version skew
|
|
65
|
+
is the likely cause.
|
|
66
|
+
2. Prefer long-form flags in generated commands.
|
|
67
|
+
3. Use `--format json` when the result will be filtered, transformed, or piped
|
|
52
68
|
into other tools.
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
69
|
+
4. Use `--summary --group-by` for aggregation before fetching large raw result sets.
|
|
70
|
+
5. Use convenience filters first, then fall back to `--filter` with `--substitution` for complex queries.
|
|
71
|
+
6. Stay scoped to the user’s requested resource or workflow.
|
|
72
|
+
7. Load deeper references only when the command surface alone is not enough.
|
|
73
|
+
8. For example authoring or review, load `example-authoring.md` before proposing YAML.
|
|
74
|
+
9. Prefer workspace IDs over names in scripted workflows when an endpoint is strict about identity.
|
|
75
|
+
10. Use `make_api_request` from `slcli.utils` for helper scripts so auth, SSL, and error handling stay consistent.
|
|
76
|
+
11. For datasheet imports, default to autonomy when the product or workspace can be resolved unambiguously.
|
|
61
77
|
|
|
62
78
|
## Common command groups
|
|
63
79
|
|
|
@@ -82,7 +98,8 @@ Load only what the current task needs.
|
|
|
82
98
|
| `user` | User management | `list`, `get`, `create`, `update` |
|
|
83
99
|
| `auth` | Authorization policies | `policy list/create`, `template list` |
|
|
84
100
|
| `workspace` | Workspaces | `list`, `get` |
|
|
85
|
-
| `
|
|
101
|
+
| `version` | CLI update hygiene | `check` |
|
|
102
|
+
| `skill` | AI skill installation | `install`, `check` |
|
|
86
103
|
| `example` | Demo provisioning | `list`, `install`, `delete` |
|
|
87
104
|
|
|
88
105
|
Example package fixtures are declared as `package` resources after a `feed`
|
|
@@ -26,6 +26,7 @@ Complete option reference for all `slcli` command groups.
|
|
|
26
26
|
- [workitem — Work item, template, and workflow management](#workitem--work-item-template-and-workflow-management)
|
|
27
27
|
- [workflow — Workflow management](#workflow--workflow-management)
|
|
28
28
|
- [webapp — Web application management](#webapp--web-application-management)
|
|
29
|
+
- [version — CLI update check](#version--cli-update-check)
|
|
29
30
|
- [skill — AI skill installation](#skill--ai-skill-installation)
|
|
30
31
|
- [example — Built-in example resource provisioning](#example--built-in-example-resource-provisioning)
|
|
31
32
|
- [Scope and capability discovery](#scope-and-capability-discovery)
|
|
@@ -946,12 +947,32 @@ metadata, carries the icon into the package, writes the matching control-file fi
|
|
|
946
947
|
generated `.nipkg`, and emits a thin `manifest.json` with `schemaVersion`, `nipkgFile`,
|
|
947
948
|
`sha256`, and any configured provenance fields.
|
|
948
949
|
|
|
950
|
+
## version — CLI update check
|
|
951
|
+
|
|
952
|
+
Compare the installed CLI with the latest PyPI release and identify the update command for
|
|
953
|
+
its installation method.
|
|
954
|
+
|
|
955
|
+
```bash
|
|
956
|
+
slcli version # Show the installed version without a network request
|
|
957
|
+
slcli version check # Check PyPI and show update guidance
|
|
958
|
+
slcli version check --format json # Structured output for automation
|
|
959
|
+
slcli version check --fail-if-outdated
|
|
960
|
+
```
|
|
961
|
+
|
|
962
|
+
`version check` detects Homebrew, Scoop, pipx, uv, pip, standalone binaries, and development
|
|
963
|
+
checkouts. By default, finding an update still exits successfully; use `--fail-if-outdated`
|
|
964
|
+
when an outdated installation should fail automation. A lookup failure exits with the network
|
|
965
|
+
error code and must not be interpreted as confirmation that the CLI is current.
|
|
966
|
+
|
|
949
967
|
## skill — AI skill installation
|
|
950
968
|
|
|
951
|
-
Install bundled skills for supported AI clients.
|
|
969
|
+
Install bundled skills for supported AI clients, or directly into a custom skills directory.
|
|
952
970
|
|
|
953
971
|
```bash
|
|
954
972
|
slcli skill install --skill [slcli|all] --client [agents|claude|all] --scope [personal|project|both]
|
|
973
|
+
slcli skill install --skill [slcli|all] --directory PATH
|
|
974
|
+
slcli skill check --skill [slcli|all] --client [agents|claude|all] --scope [personal|project|both]
|
|
975
|
+
slcli skill check --skill [slcli|all] --directory PATH
|
|
955
976
|
```
|
|
956
977
|
|
|
957
978
|
Client paths:
|
|
@@ -963,6 +984,22 @@ Client paths:
|
|
|
963
984
|
Notes:
|
|
964
985
|
|
|
965
986
|
- `agents` is the default client in interactive mode.
|
|
987
|
+
- `--directory` is the directory that directly contains skill folders and cannot be combined
|
|
988
|
+
with `--client` or `--scope`.
|
|
989
|
+
- Installed skills carry the `slcli` release version. `skill check` exits nonzero if a skill is
|
|
990
|
+
missing, predates the bundled version, or was installed before version metadata was added.
|
|
991
|
+
- Use `skill install --force` to update an existing installation.
|
|
992
|
+
- For hygiene checks, specify the client and scope that contain the active skill, then use the
|
|
993
|
+
same target to update it:
|
|
994
|
+
|
|
995
|
+
```bash
|
|
996
|
+
slcli skill check --client agents --scope project
|
|
997
|
+
slcli skill install --client agents --scope project --force
|
|
998
|
+
```
|
|
999
|
+
|
|
1000
|
+
Substitute `claude`, `personal`, or `--directory PATH` when that is where the active skill is
|
|
1001
|
+
installed. Checking the wrong target can report a current skill as missing.
|
|
1002
|
+
|
|
966
1003
|
- The bundled `slcli` skill now covers the previous standalone workflow skills.
|
|
967
1004
|
|
|
968
1005
|
## example — Built-in example resource provisioning
|
{systemlink_cli-1.27.2 → systemlink_cli-1.27.3}/slcli/skills/slcli/references/troubleshooting.md
RENAMED
|
@@ -2,6 +2,28 @@
|
|
|
2
2
|
|
|
3
3
|
Common issues and workarounds when using `slcli` or scripting against SystemLink APIs.
|
|
4
4
|
|
|
5
|
+
## Update preflight
|
|
6
|
+
|
|
7
|
+
Before deeper troubleshooting, check the CLI and the active installed skill once per session:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
slcli version check
|
|
11
|
+
slcli skill check --client <CLIENT> --scope <SCOPE>
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
Use `--directory <PATH>` instead of `--client` and `--scope` for a custom skill location. If
|
|
15
|
+
the CLI is outdated, recommend the manager-specific command printed by `version check`. If the
|
|
16
|
+
skill check reports `missing`, `unversioned`, or `outdated`, recommend updating the same target:
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
slcli skill install --client <CLIENT> --scope <SCOPE> --force
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Obtain the user's approval before running an update. If either check fails because the command
|
|
23
|
+
is unavailable or the network cannot be reached, note that version status is unknown and
|
|
24
|
+
continue troubleshooting. Give version skew more weight when the failure involves unknown
|
|
25
|
+
commands, changed options, output shape, packaging, or skill instructions.
|
|
26
|
+
|
|
5
27
|
## Effective profile and workspace scope
|
|
6
28
|
|
|
7
29
|
Resolve the workspace and profile independently before querying resources. A
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
"""Version checking and update guidance for slcli."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import sys
|
|
6
|
+
from dataclasses import asdict, dataclass
|
|
7
|
+
from enum import Enum
|
|
8
|
+
from importlib.metadata import PackageNotFoundError, distribution
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from typing import Any, Optional
|
|
11
|
+
|
|
12
|
+
import click
|
|
13
|
+
import requests
|
|
14
|
+
from packaging.version import InvalidVersion, Version
|
|
15
|
+
|
|
16
|
+
from ._version import __version__
|
|
17
|
+
from .rich_output import print_json
|
|
18
|
+
from .utils import ExitCodes
|
|
19
|
+
|
|
20
|
+
PYPI_URL = "https://pypi.org/pypi/systemlink-cli/json"
|
|
21
|
+
RELEASES_URL = "https://github.com/ni-kismet/systemlink-cli/releases/latest"
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class InstallMethod(str, Enum):
|
|
25
|
+
"""Supported slcli installation methods."""
|
|
26
|
+
|
|
27
|
+
HOMEBREW = "homebrew"
|
|
28
|
+
SCOOP = "scoop"
|
|
29
|
+
PIPX = "pipx"
|
|
30
|
+
UV = "uv"
|
|
31
|
+
PIP = "pip"
|
|
32
|
+
STANDALONE = "standalone"
|
|
33
|
+
DEVELOPMENT = "development"
|
|
34
|
+
UNKNOWN = "unknown"
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@dataclass(frozen=True)
|
|
38
|
+
class VersionCheckResult:
|
|
39
|
+
"""Result returned by a version check.
|
|
40
|
+
|
|
41
|
+
Attributes:
|
|
42
|
+
current_version: Installed slcli version.
|
|
43
|
+
latest_version: Latest version published to PyPI.
|
|
44
|
+
status: One of ``outdated``, ``current``, or ``ahead``.
|
|
45
|
+
install_method: Detected installation method.
|
|
46
|
+
update_command: Suggested update command, when available.
|
|
47
|
+
"""
|
|
48
|
+
|
|
49
|
+
current_version: str
|
|
50
|
+
latest_version: str
|
|
51
|
+
status: str
|
|
52
|
+
install_method: str
|
|
53
|
+
update_command: Optional[str]
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def fetch_latest_version() -> str:
|
|
57
|
+
"""Return the latest published systemlink-cli version from PyPI."""
|
|
58
|
+
response = requests.get(PYPI_URL, timeout=5)
|
|
59
|
+
response.raise_for_status()
|
|
60
|
+
payload: Any = response.json()
|
|
61
|
+
try:
|
|
62
|
+
version = payload["info"]["version"]
|
|
63
|
+
except (KeyError, TypeError) as exc:
|
|
64
|
+
raise ValueError("PyPI returned an unexpected response") from exc
|
|
65
|
+
if not isinstance(version, str) or not version:
|
|
66
|
+
raise ValueError("PyPI returned an invalid version")
|
|
67
|
+
try:
|
|
68
|
+
Version(version)
|
|
69
|
+
except InvalidVersion as exc:
|
|
70
|
+
raise ValueError(f"PyPI returned an invalid version: {version}") from exc
|
|
71
|
+
return version
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def _path_contains(path: Path, *parts: str) -> bool:
|
|
75
|
+
"""Return whether a path contains the given consecutive components."""
|
|
76
|
+
lowered_parts = tuple(part.lower() for part in path.parts)
|
|
77
|
+
expected = tuple(part.lower() for part in parts)
|
|
78
|
+
width = len(expected)
|
|
79
|
+
return any(lowered_parts[index : index + width] == expected for index in range(len(path.parts)))
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def detect_install_method() -> InstallMethod:
|
|
83
|
+
"""Detect how the running slcli installation is managed."""
|
|
84
|
+
executable = Path(sys.executable).resolve()
|
|
85
|
+
|
|
86
|
+
if getattr(sys, "frozen", False):
|
|
87
|
+
if _path_contains(executable, "cellar", "slcli"):
|
|
88
|
+
return InstallMethod.HOMEBREW
|
|
89
|
+
if _path_contains(executable, "scoop", "apps", "slcli"):
|
|
90
|
+
return InstallMethod.SCOOP
|
|
91
|
+
return InstallMethod.STANDALONE
|
|
92
|
+
|
|
93
|
+
if _path_contains(executable, "pipx", "venvs", "systemlink-cli"):
|
|
94
|
+
return InstallMethod.PIPX
|
|
95
|
+
if _path_contains(executable, "uv", "tools", "systemlink-cli"):
|
|
96
|
+
return InstallMethod.UV
|
|
97
|
+
|
|
98
|
+
project_root = Path(__file__).resolve().parent.parent
|
|
99
|
+
if (project_root / "pyproject.toml").is_file() and (project_root / ".git").exists():
|
|
100
|
+
return InstallMethod.DEVELOPMENT
|
|
101
|
+
|
|
102
|
+
try:
|
|
103
|
+
distribution("systemlink-cli")
|
|
104
|
+
except PackageNotFoundError:
|
|
105
|
+
return InstallMethod.UNKNOWN
|
|
106
|
+
return InstallMethod.PIP
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def get_update_command(install_method: InstallMethod) -> Optional[str]:
|
|
110
|
+
"""Return the update command for an installation method, when available."""
|
|
111
|
+
commands = {
|
|
112
|
+
InstallMethod.HOMEBREW: "brew upgrade slcli",
|
|
113
|
+
InstallMethod.SCOOP: "scoop update slcli",
|
|
114
|
+
InstallMethod.PIPX: "pipx upgrade systemlink-cli",
|
|
115
|
+
InstallMethod.UV: "uv tool upgrade systemlink-cli",
|
|
116
|
+
InstallMethod.PIP: f'"{sys.executable}" -m pip install --upgrade systemlink-cli',
|
|
117
|
+
InstallMethod.DEVELOPMENT: "git pull && poetry install",
|
|
118
|
+
}
|
|
119
|
+
return commands.get(install_method)
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def check_version(current_version: str = __version__) -> VersionCheckResult:
|
|
123
|
+
"""Compare the current slcli version with the latest published version."""
|
|
124
|
+
latest_version = fetch_latest_version()
|
|
125
|
+
current = Version(current_version)
|
|
126
|
+
latest = Version(latest_version)
|
|
127
|
+
status = "outdated" if current < latest else "current" if current == latest else "ahead"
|
|
128
|
+
install_method = detect_install_method()
|
|
129
|
+
return VersionCheckResult(
|
|
130
|
+
current_version=current_version,
|
|
131
|
+
latest_version=latest_version,
|
|
132
|
+
status=status,
|
|
133
|
+
install_method=install_method.value,
|
|
134
|
+
update_command=get_update_command(install_method),
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def _render_version_check(result: VersionCheckResult) -> None:
|
|
139
|
+
"""Render a human-readable version check result."""
|
|
140
|
+
click.echo(f"Current version: {result.current_version}")
|
|
141
|
+
click.echo(f"Latest version: {result.latest_version}")
|
|
142
|
+
click.echo(f"Installed with: {result.install_method}")
|
|
143
|
+
click.echo()
|
|
144
|
+
|
|
145
|
+
if result.status == "current":
|
|
146
|
+
click.echo("✓ slcli is up to date")
|
|
147
|
+
elif result.status == "ahead":
|
|
148
|
+
click.echo("Current version is newer than the latest published version")
|
|
149
|
+
elif result.update_command:
|
|
150
|
+
click.echo("A newer version is available:")
|
|
151
|
+
click.echo(f" {result.update_command}")
|
|
152
|
+
else:
|
|
153
|
+
click.echo("A newer version is available.")
|
|
154
|
+
click.echo(f"Download it from {RELEASES_URL}")
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
def register_version_commands(cli: Any) -> None:
|
|
158
|
+
"""Register version commands with the CLI."""
|
|
159
|
+
|
|
160
|
+
@cli.group(invoke_without_command=True)
|
|
161
|
+
@click.pass_context
|
|
162
|
+
def version(ctx: click.Context) -> None:
|
|
163
|
+
"""Show the installed version and check for updates."""
|
|
164
|
+
if ctx.invoked_subcommand is None:
|
|
165
|
+
click.echo(f"slcli version {__version__}")
|
|
166
|
+
|
|
167
|
+
@version.command(name="check")
|
|
168
|
+
@click.option(
|
|
169
|
+
"--format",
|
|
170
|
+
"-f",
|
|
171
|
+
"format_output",
|
|
172
|
+
type=click.Choice(["text", "json"]),
|
|
173
|
+
default="text",
|
|
174
|
+
show_default=True,
|
|
175
|
+
help="Output format",
|
|
176
|
+
)
|
|
177
|
+
@click.option(
|
|
178
|
+
"--fail-if-outdated",
|
|
179
|
+
is_flag=True,
|
|
180
|
+
help="Exit with code 1 when a newer version is available",
|
|
181
|
+
)
|
|
182
|
+
def check(format_output: str, fail_if_outdated: bool) -> None:
|
|
183
|
+
"""Check whether this is the latest slcli version."""
|
|
184
|
+
try:
|
|
185
|
+
result = check_version()
|
|
186
|
+
except InvalidVersion as exc:
|
|
187
|
+
click.echo(f"✗ Invalid version: {exc}", err=True)
|
|
188
|
+
sys.exit(ExitCodes.INVALID_INPUT)
|
|
189
|
+
except (requests.RequestException, ValueError) as exc:
|
|
190
|
+
click.echo(f"✗ Unable to retrieve the latest slcli version: {exc}", err=True)
|
|
191
|
+
sys.exit(ExitCodes.NETWORK_ERROR)
|
|
192
|
+
|
|
193
|
+
if format_output == "json":
|
|
194
|
+
print_json(asdict(result))
|
|
195
|
+
else:
|
|
196
|
+
_render_version_check(result)
|
|
197
|
+
|
|
198
|
+
if fail_if_outdated and result.status == "outdated":
|
|
199
|
+
sys.exit(ExitCodes.GENERAL_ERROR)
|
|
@@ -20,6 +20,7 @@ import questionary
|
|
|
20
20
|
import requests
|
|
21
21
|
|
|
22
22
|
from .cli_utils import is_interactive_environment, validate_output_format
|
|
23
|
+
from .platform import PLATFORM_SLS, get_platform
|
|
23
24
|
from .skill_click import install_skills_to_directory
|
|
24
25
|
from .universal_handlers import UniversalResponseHandler
|
|
25
26
|
from .utils import (
|
|
@@ -96,8 +97,9 @@ def _build_published_webapp_url(
|
|
|
96
97
|
) -> str:
|
|
97
98
|
"""Return the best available published URL for a webapp.
|
|
98
99
|
|
|
99
|
-
|
|
100
|
-
|
|
100
|
+
Uses the WebVI host route for SystemLink Server and otherwise prefers the
|
|
101
|
+
friendly SystemLink web UI URL. Falls back to any explicit URL-like property
|
|
102
|
+
returned by the service, then the raw content endpoint.
|
|
101
103
|
"""
|
|
102
104
|
from urllib.parse import quote
|
|
103
105
|
|
|
@@ -133,6 +135,10 @@ def _build_published_webapp_url(
|
|
|
133
135
|
if mapped_workspace_name:
|
|
134
136
|
workspace_name = mapped_workspace_name
|
|
135
137
|
|
|
138
|
+
if get_platform() == PLATFORM_SLS and webapp_id:
|
|
139
|
+
web_base = get_web_url().rstrip("/")
|
|
140
|
+
return f"{web_base}/#/webvihost/view/webvi/{quote(webapp_id, safe='')}"
|
|
141
|
+
|
|
136
142
|
if resolved_name and workspace_name:
|
|
137
143
|
web_base = get_web_url().rstrip("/")
|
|
138
144
|
return f"{web_base}/webapps/app/{quote(workspace_name)}/{quote(resolved_name)}"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|