systemlink-cli 1.30.3__tar.gz → 2.2.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.30.3 → systemlink_cli-2.2.2}/PKG-INFO +7 -1
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/pyproject.toml +10 -3
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/_version.py +1 -1
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/config_click.py +189 -93
- systemlink_cli-2.2.2/slcli/credentials.py +511 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/function_click.py +1 -1
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/main.py +98 -80
- systemlink_cli-2.2.2/slcli/managed_client/__init__.py +63 -0
- systemlink_cli-2.2.2/slcli/managed_client/crypto.py +433 -0
- systemlink_cli-2.2.2/slcli/managed_client/handlers.py +415 -0
- systemlink_cli-2.2.2/slcli/managed_client/minion.py +543 -0
- systemlink_cli-2.2.2/slcli/managed_client/models.py +100 -0
- systemlink_cli-2.2.2/slcli/managed_client/protocol.py +485 -0
- systemlink_cli-2.2.2/slcli/managed_client/rest.py +273 -0
- systemlink_cli-2.2.2/slcli/managed_client/state.py +328 -0
- systemlink_cli-2.2.2/slcli/managed_client/transport.py +139 -0
- systemlink_cli-2.2.2/slcli/managed_client_click.py +320 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/pkce.py +112 -102
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/platform.py +35 -50
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/policy_click.py +158 -1
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/policy_utils.py +17 -5
- systemlink_cli-2.2.2/slcli/profile_credentials.py +444 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/profiles.py +79 -118
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/rich_output.py +87 -19
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/skills/slcli/references/commands.md +27 -4
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/utils.py +114 -128
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/LICENSE +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/dff-editor/editor.js +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/dff-editor/index.html +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/__init__.py +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/__main__.py +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/alarm_click.py +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/asset_click.py +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/cli_formatters.py +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/cli_utils.py +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/comment_click.py +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/completion_click.py +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/config.py +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/dataframe_click.py +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/dff_click.py +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/dff_decorators.py +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/example_click.py +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/example_loader.py +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/example_provisioner.py +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/examples/README.md +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/examples/_schema/schema-v1.0.json +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/examples/demo-data-1/README.md +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/examples/demo-data-1/config.yaml +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/examples/demo-data-2/README.md +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/examples/demo-data-2/config.yaml +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/examples/demo-data-3/config.yaml +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/examples/demo-data-3/leakage-current-results.json +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/examples/demo-data-3/overvoltage-results.json +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/examples/demo-data-3/product-xyz-compliance-report.json +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/examples/demo-data-3/product-xyz-specification.csv +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/examples/demo-data-3/thermal-response-results.json +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/examples/exercise-5-1-parametric-insights/README.md +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/examples/exercise-5-1-parametric-insights/config.yaml +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/examples/exercise-7-1-test-plans/README.md +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/examples/exercise-7-1-test-plans/config.yaml +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/examples/spec-compliance-notebooks/README.md +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/examples/spec-compliance-notebooks/config.yaml +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/examples/spec-compliance-notebooks/notebooks/SpecAnalysis_ComplianceCalculation.ipynb +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/examples/spec-compliance-notebooks/notebooks/SpecComplianceCalculation.ipynb +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/examples/spec-compliance-notebooks/notebooks/SpecfileExtractionAndIngestion.ipynb +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/examples/spec-compliance-notebooks/spec_template.xlsx +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/feed_click.py +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/file_click.py +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/function_templates.py +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/mcp_click.py +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/mcp_reachability.py +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/mcp_server.py +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/mcp_skills.py +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/notebook_click.py +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/response_handlers.py +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/routine_click.py +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/skill_click.py +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/skills/slcli/SKILL.md +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/skills/slcli/evals/COPILOT_BATCH_RUN_PROMPT.md +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/skills/slcli/evals/README.md +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/skills/slcli/evals/evals.json +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/skills/slcli/evals/evals.schema.json +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/skills/slcli/evals/files/spec-import/battery-specs.csv +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/skills/slcli/evals/files/webapp-package/icon.svg +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/skills/slcli/evals/files/webapp-package/nipkg.config.json +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/skills/slcli/evals/files/webapp-package/webapp-content/index.html +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/skills/slcli/evals/trigger_evals.json +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/skills/slcli/references/analysis-recipes.md +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/skills/slcli/references/datasheet-workflow.md +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/skills/slcli/references/domain-concepts.md +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/skills/slcli/references/example-authoring.md +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/skills/slcli/references/filtering.md +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/skills/slcli/references/import-specs.min.json +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/skills/slcli/references/job-debugging/overview.md +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/skills/slcli/references/nipkg/overview.md +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/skills/slcli/references/notebook/interfaces.md +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/skills/slcli/references/notebook/notebook-patterns.md +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/skills/slcli/references/notebook/overview.md +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/skills/slcli/references/python-test/overview.md +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/skills/slcli/references/routine-examples.md +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/skills/slcli/references/troubleshooting.md +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/skills/slcli/references/webapp/angular-ui-packages.md +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/skills/slcli/references/webapp/deployment.md +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/skills/slcli/references/webapp/layout-patterns.md +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/skills/slcli/references/webapp/nimble-angular.md +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/skills/slcli/references/webapp/overview.md +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/skills/slcli/references/webapp/systemlink-services.md +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/skills/slcli/references/webapp/troubleshooting.md +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/skills/slcli/scripts/benchmark_iteration.py +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/skills/slcli/scripts/compare_iteration.py +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/skills/slcli/scripts/eval_manifest.py +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/skills/slcli/scripts/grade_eval_response.py +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/skills/slcli/scripts/grade_iteration.py +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/skills/slcli/scripts/prepare_eval_prompts.py +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/skills/slcli/scripts/prepare_eval_workspace.py +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/skills/slcli/scripts/render_eval_review.py +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/skills/slcli/scripts/spec_import_helper.py +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/spec_click.py +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/ssl_trust.py +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/state_click.py +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/system_click.py +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/system_query_utils.py +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/systemlink-logo.svg +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/table_utils.py +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/tag_click.py +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/templates_click.py +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/testmonitor_click.py +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/universal_handlers.py +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/user_click.py +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/version_click.py +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/web_editor.py +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/webapp_bootstrap.py +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/webapp_click.py +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/webapp_templates/angular/blank/.gitignore +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/webapp_templates/angular/blank/README.md +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/webapp_templates/angular/blank/angular.json +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/webapp_templates/angular/blank/package.json +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/webapp_templates/angular/blank/scripts/filter-sbom.js +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/webapp_templates/angular/blank/src/app/app-routing.module.ts +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/webapp_templates/angular/blank/src/app/app.component.html +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/webapp_templates/angular/blank/src/app/app.component.scss +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/webapp_templates/angular/blank/src/app/app.component.ts +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/webapp_templates/angular/blank/src/app/app.module.ts +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/webapp_templates/angular/blank/src/app/core/layout/app-shell.component.html +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/webapp_templates/angular/blank/src/app/core/layout/app-shell.component.scss +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/webapp_templates/angular/blank/src/app/core/layout/app-shell.component.ts +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/webapp_templates/angular/blank/src/app/core/state/app-view-state.service.ts +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/webapp_templates/angular/blank/src/app/core/systemlink/systemlink-context.service.ts +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/webapp_templates/angular/blank/src/app/core/systemlink/theme-sync.service.ts +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/webapp_templates/angular/blank/src/app/core/systemlink/webapp-home-data.service.ts +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/webapp_templates/angular/blank/src/app/features/assets/assets-page.component.html +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/webapp_templates/angular/blank/src/app/features/assets/assets-page.component.scss +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/webapp_templates/angular/blank/src/app/features/assets/assets-page.component.ts +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/webapp_templates/angular/blank/src/app/features/datasets/datasets-page.component.html +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/webapp_templates/angular/blank/src/app/features/datasets/datasets-page.component.scss +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/webapp_templates/angular/blank/src/app/features/datasets/datasets-page.component.ts +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/webapp_templates/angular/blank/src/app/features/home/home-page.component.html +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/webapp_templates/angular/blank/src/app/features/home/home-page.component.scss +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/webapp_templates/angular/blank/src/app/features/home/home-page.component.ts +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/webapp_templates/angular/blank/src/app/features/master-detail/master-detail-page.component.html +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/webapp_templates/angular/blank/src/app/features/master-detail/master-detail-page.component.scss +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/webapp_templates/angular/blank/src/app/features/master-detail/master-detail-page.component.ts +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/webapp_templates/angular/blank/src/app/features/operations/operations-page.component.html +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/webapp_templates/angular/blank/src/app/features/operations/operations-page.component.scss +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/webapp_templates/angular/blank/src/app/features/operations/operations-page.component.ts +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/webapp_templates/angular/blank/src/app/features/settings/settings-page.component.html +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/webapp_templates/angular/blank/src/app/features/settings/settings-page.component.scss +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/webapp_templates/angular/blank/src/app/features/settings/settings-page.component.ts +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/webapp_templates/angular/blank/src/app/shared/components/empty-state.component.html +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/webapp_templates/angular/blank/src/app/shared/components/empty-state.component.scss +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/webapp_templates/angular/blank/src/app/shared/components/empty-state.component.ts +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/webapp_templates/angular/blank/src/app/shared/components/error-banner.component.html +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/webapp_templates/angular/blank/src/app/shared/components/error-banner.component.scss +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/webapp_templates/angular/blank/src/app/shared/components/error-banner.component.ts +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/webapp_templates/angular/blank/src/app/shared/components/loading-state.component.html +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/webapp_templates/angular/blank/src/app/shared/components/loading-state.component.scss +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/webapp_templates/angular/blank/src/app/shared/components/loading-state.component.ts +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/webapp_templates/angular/blank/src/app/shared/states/view-state.model.ts +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/webapp_templates/angular/blank/src/assets/README.md +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/webapp_templates/angular/blank/src/index.html +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/webapp_templates/angular/blank/src/main.ts +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/webapp_templates/angular/blank/src/styles.scss +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/webapp_templates/angular/blank/tsconfig.app.json +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/webapp_templates/angular/blank/tsconfig.json +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/workflow_preview.py +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/workflows_click.py +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/workitem_click.py +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/workspace_click.py +0 -0
- {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/workspace_utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: systemlink-cli
|
|
3
|
-
Version:
|
|
3
|
+
Version: 2.2.2
|
|
4
4
|
Summary: SystemLink Integrator CLI - cross-platform CLI for SystemLink workflows and templates.
|
|
5
5
|
License-File: LICENSE
|
|
6
6
|
Author: Fred Visser
|
|
@@ -10,11 +10,17 @@ Classifier: Programming Language :: Python :: 3
|
|
|
10
10
|
Classifier: Programming Language :: Python :: 3.12
|
|
11
11
|
Classifier: Programming Language :: Python :: 3.13
|
|
12
12
|
Classifier: Programming Language :: Python :: 3.14
|
|
13
|
+
Provides-Extra: managed-client
|
|
13
14
|
Provides-Extra: mcp
|
|
15
|
+
Requires-Dist: anyio (>=4.14.2,<5) ; extra == "mcp"
|
|
14
16
|
Requires-Dist: click (>=7.1.2)
|
|
15
17
|
Requires-Dist: cryptography (>=49.0.0)
|
|
18
|
+
Requires-Dist: filelock (>=3.20,<4)
|
|
19
|
+
Requires-Dist: httpcore2 (>=2.10.0,<3) ; extra == "mcp"
|
|
20
|
+
Requires-Dist: httpx2 (>=2.12.0,<3) ; extra == "mcp"
|
|
16
21
|
Requires-Dist: keyring (>=25.6.0,<26.0.0)
|
|
17
22
|
Requires-Dist: mcp (>=2,<3) ; extra == "mcp"
|
|
23
|
+
Requires-Dist: msgpack (>=1.1,<2) ; extra == "managed-client"
|
|
18
24
|
Requires-Dist: packaging (>=21.0)
|
|
19
25
|
Requires-Dist: pygments (>=2.20.0)
|
|
20
26
|
Requires-Dist: pyyaml (>=6.0.3,<7.0.0)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "systemlink-cli"
|
|
3
|
-
version = "
|
|
3
|
+
version = "2.2.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" }]
|
|
@@ -26,6 +26,7 @@ lint = "scripts.lint:main"
|
|
|
26
26
|
python = ">=3.11.1,<3.15"
|
|
27
27
|
# cli
|
|
28
28
|
click = ">=7.1.2"
|
|
29
|
+
filelock = ">=3.20,<4"
|
|
29
30
|
keyring = "^25.6.0"
|
|
30
31
|
requests = "^2.32.4"
|
|
31
32
|
tabulate = "^0.10.0"
|
|
@@ -38,10 +39,15 @@ rich = ">=15,<16"
|
|
|
38
39
|
rich-click = ">=1.8,<2"
|
|
39
40
|
cryptography = ">=49.0.0"
|
|
40
41
|
pygments = ">=2.20.0"
|
|
42
|
+
msgpack = { version = ">=1.1,<2", optional = true }
|
|
41
43
|
mcp = { version = ">=2,<3", optional = true }
|
|
44
|
+
anyio = { version = ">=4.14.2,<5", optional = true }
|
|
45
|
+
httpx2 = { version = ">=2.12.0,<3", optional = true }
|
|
46
|
+
httpcore2 = { version = ">=2.10.0,<3", optional = true }
|
|
42
47
|
|
|
43
48
|
[tool.poetry.extras]
|
|
44
|
-
|
|
49
|
+
managed-client = ["msgpack"]
|
|
50
|
+
mcp = ["mcp", "anyio", "httpx2", "httpcore2"]
|
|
45
51
|
|
|
46
52
|
|
|
47
53
|
[tool.poetry.group.dev.dependencies]
|
|
@@ -69,13 +75,14 @@ pytest-cov = ">=3.0"
|
|
|
69
75
|
pytest-timeout = ">=2.1.0"
|
|
70
76
|
pyinstaller = "^6.14.2"
|
|
71
77
|
jsonschema = ">=4.0"
|
|
78
|
+
msgpack = ">=1.1,<2"
|
|
72
79
|
|
|
73
80
|
# Typed stubs for third-party libraries used by mypy in CI
|
|
74
81
|
types-requests = "*"
|
|
75
82
|
types-tabulate = "*"
|
|
76
83
|
|
|
77
84
|
# Changelog and release management
|
|
78
|
-
towncrier = "^
|
|
85
|
+
towncrier = "^26.0.0"
|
|
79
86
|
pytest-xdist = "^3.8.0"
|
|
80
87
|
|
|
81
88
|
# Excel file generation
|
|
@@ -12,13 +12,18 @@ from urllib.parse import urlparse
|
|
|
12
12
|
import click
|
|
13
13
|
import questionary
|
|
14
14
|
|
|
15
|
+
from .credentials import (
|
|
16
|
+
CredentialStoreError,
|
|
17
|
+
describe_credential_store,
|
|
18
|
+
get_credential,
|
|
19
|
+
)
|
|
15
20
|
from .platform import (
|
|
16
21
|
PLATFORM_SLE,
|
|
17
22
|
PLATFORM_SLS,
|
|
18
23
|
check_service_status,
|
|
19
24
|
check_web_server_auth,
|
|
20
25
|
)
|
|
21
|
-
from .profiles import
|
|
26
|
+
from .profiles import Profile, ProfileConfig, check_config_file_permissions
|
|
22
27
|
from .rich_output import render_table
|
|
23
28
|
from .ssl_trust import (
|
|
24
29
|
get_managed_trust_records,
|
|
@@ -33,9 +38,9 @@ from .utils import ExitCodes, get_base_url, get_base_url_resolution
|
|
|
33
38
|
API_KEY_LENGTH = 42
|
|
34
39
|
API_KEY_PATTERN = re.compile(rf"^[A-Za-z0-9_-]{{{API_KEY_LENGTH}}}$")
|
|
35
40
|
ENV_OVERRIDE_FIELDS = (
|
|
36
|
-
("API URL", ("SLCLI_API_URL",
|
|
37
|
-
("API Key", ("SLCLI_API_KEY",
|
|
38
|
-
("Web URL", ("SLCLI_WEB_URL",
|
|
41
|
+
("API URL", ("SLCLI_API_URL",)),
|
|
42
|
+
("API Key", ("SLCLI_API_KEY",)),
|
|
43
|
+
("Web URL", ("SLCLI_WEB_URL",)),
|
|
39
44
|
)
|
|
40
45
|
|
|
41
46
|
|
|
@@ -224,6 +229,7 @@ def _add_profile_impl(
|
|
|
224
229
|
client_id: Optional[str] = None,
|
|
225
230
|
scopes: tuple[str, ...] = (),
|
|
226
231
|
callback_port: Optional[int] = None,
|
|
232
|
+
credential_store: str = "os",
|
|
227
233
|
) -> None:
|
|
228
234
|
"""Shared implementation for add-profile and login commands.
|
|
229
235
|
|
|
@@ -243,6 +249,7 @@ def _add_profile_impl(
|
|
|
243
249
|
client_id: Public OAuth client ID for the PKCE login
|
|
244
250
|
scopes: OAuth scopes for the PKCE login
|
|
245
251
|
callback_port: Optional loopback callback port; zero selects an ephemeral port
|
|
252
|
+
credential_store: Store for profile API keys and PKCE credentials
|
|
246
253
|
"""
|
|
247
254
|
# Get profile name
|
|
248
255
|
if not profile:
|
|
@@ -410,10 +417,11 @@ def _add_profile_impl(
|
|
|
410
417
|
workspace = workspace_input if workspace_input else None
|
|
411
418
|
|
|
412
419
|
# Create profile
|
|
420
|
+
cfg = ProfileConfig.load()
|
|
413
421
|
new_profile = Profile(
|
|
414
422
|
name=profile,
|
|
415
423
|
server=url,
|
|
416
|
-
api_key=api_key,
|
|
424
|
+
api_key=api_key or "",
|
|
417
425
|
web_url=web_url,
|
|
418
426
|
platform=platform,
|
|
419
427
|
workspace=workspace,
|
|
@@ -421,36 +429,25 @@ def _add_profile_impl(
|
|
|
421
429
|
auth_mode=auth_mode,
|
|
422
430
|
pkce_client_id=client_id if auth_mode == "pkce" else None,
|
|
423
431
|
pkce_scopes=pkce_scopes if auth_mode == "pkce" else None,
|
|
432
|
+
credential_store=credential_store,
|
|
424
433
|
)
|
|
425
|
-
|
|
426
|
-
# Load config and add profile
|
|
427
|
-
cfg = ProfileConfig.load()
|
|
428
|
-
previous_profile = cfg.get_profile(profile)
|
|
429
|
-
previous_current_profile = cfg.current_profile
|
|
430
|
-
cfg.add_profile(new_profile, set_current=set_current)
|
|
431
|
-
cfg.save()
|
|
432
|
-
|
|
433
434
|
if auth_mode == "pkce" and pkce_result is not None:
|
|
434
|
-
|
|
435
|
+
new_profile.pkce_credentials = {
|
|
436
|
+
"access-token": pkce_result.access_token,
|
|
437
|
+
"refresh-token": pkce_result.refresh_token,
|
|
438
|
+
"access-expires-at": pkce_result.expires_at,
|
|
439
|
+
}
|
|
435
440
|
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
)
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
else:
|
|
447
|
-
cfg.profiles[profile] = previous_profile
|
|
448
|
-
cfg.current_profile = previous_current_profile
|
|
449
|
-
cfg.save()
|
|
450
|
-
_exit_with_validation_error(
|
|
451
|
-
f"Could not store PKCE credentials securely: {exc}.",
|
|
452
|
-
ExitCodes.GENERAL_ERROR,
|
|
453
|
-
)
|
|
441
|
+
from .profile_credentials import ProfileCredentialError, save_profile_credentials
|
|
442
|
+
|
|
443
|
+
try:
|
|
444
|
+
warnings = save_profile_credentials(cfg, new_profile, set_current=set_current)
|
|
445
|
+
except ProfileCredentialError as exc:
|
|
446
|
+
for warning in exc.warnings:
|
|
447
|
+
click.echo(f"⚠️ {warning}", err=True)
|
|
448
|
+
_exit_with_validation_error(str(exc), ExitCodes.GENERAL_ERROR)
|
|
449
|
+
for warning in warnings:
|
|
450
|
+
click.echo(f"⚠️ {warning}", err=True)
|
|
454
451
|
|
|
455
452
|
click.echo(f"\n✓ Profile '{profile}' saved successfully.")
|
|
456
453
|
click.echo(f" Server: {url}")
|
|
@@ -464,6 +461,14 @@ def _add_profile_impl(
|
|
|
464
461
|
click.echo(f"\nConfig file: {ProfileConfig.get_config_path()}")
|
|
465
462
|
|
|
466
463
|
|
|
464
|
+
def _get_profile_secret(profile: Profile, credential: str) -> Optional[str]:
|
|
465
|
+
"""Read a profile secret only for an explicit secret-display operation."""
|
|
466
|
+
try:
|
|
467
|
+
return get_credential(profile.credential_id, credential, profile.credential_store)
|
|
468
|
+
except CredentialStoreError as exc:
|
|
469
|
+
raise click.ClickException(str(exc)) from exc
|
|
470
|
+
|
|
471
|
+
|
|
467
472
|
def register_config_commands(cli: Any) -> None:
|
|
468
473
|
"""Register the 'config' command group and its subcommands."""
|
|
469
474
|
|
|
@@ -582,16 +587,15 @@ def register_config_commands(cli: Any) -> None:
|
|
|
582
587
|
@click.argument("name")
|
|
583
588
|
def use_profile(name: str) -> None:
|
|
584
589
|
"""Switch to a different profile."""
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
cfg.save()
|
|
590
|
+
with ProfileConfig().transaction() as cfg:
|
|
591
|
+
if name not in cfg.profiles:
|
|
592
|
+
click.echo(f"✗ Profile '{name}' not found.", err=True)
|
|
593
|
+
if cfg.profiles:
|
|
594
|
+
click.echo(f"Available profiles: {', '.join(cfg.profiles.keys())}", err=True)
|
|
595
|
+
sys.exit(ExitCodes.NOT_FOUND)
|
|
596
|
+
|
|
597
|
+
cfg.set_current_profile(name)
|
|
598
|
+
cfg.save()
|
|
595
599
|
|
|
596
600
|
profile = cfg.get_profile(name)
|
|
597
601
|
click.echo(f"✓ Switched to profile '{name}'")
|
|
@@ -627,8 +631,37 @@ def register_config_commands(cli: Any) -> None:
|
|
|
627
631
|
data["profiles"] = {}
|
|
628
632
|
for name, profile in cfg.profiles.items():
|
|
629
633
|
profile_dict = profile.to_dict()
|
|
630
|
-
if
|
|
631
|
-
|
|
634
|
+
if profile.auth_mode == "pkce":
|
|
635
|
+
if show_secrets:
|
|
636
|
+
try:
|
|
637
|
+
pkce_credentials = json.loads(
|
|
638
|
+
_get_profile_secret(profile, "pkce") or "{}"
|
|
639
|
+
)
|
|
640
|
+
except json.JSONDecodeError:
|
|
641
|
+
_exit_with_validation_error(
|
|
642
|
+
f"Stored PKCE credentials for profile '{name}' are invalid. "
|
|
643
|
+
f"Run 'slcli login --profile {name}' to authenticate again.",
|
|
644
|
+
ExitCodes.GENERAL_ERROR,
|
|
645
|
+
)
|
|
646
|
+
if not isinstance(pkce_credentials, dict):
|
|
647
|
+
_exit_with_validation_error(
|
|
648
|
+
f"Stored PKCE credentials for profile '{name}' are invalid. "
|
|
649
|
+
f"Run 'slcli login --profile {name}' to authenticate again.",
|
|
650
|
+
ExitCodes.GENERAL_ERROR,
|
|
651
|
+
)
|
|
652
|
+
profile_dict["pkce-credentials"] = pkce_credentials
|
|
653
|
+
else:
|
|
654
|
+
profile_dict["pkce-credentials"] = (
|
|
655
|
+
f"stored in {describe_credential_store(profile.credential_store)}"
|
|
656
|
+
)
|
|
657
|
+
elif profile.credential_store == "os" and not profile.api_key:
|
|
658
|
+
if show_secrets:
|
|
659
|
+
profile_dict["api-key"] = _get_profile_secret(profile, "api-key")
|
|
660
|
+
else:
|
|
661
|
+
profile_dict["api-key"] = (
|
|
662
|
+
f"stored in {describe_credential_store(profile.credential_store)}"
|
|
663
|
+
)
|
|
664
|
+
elif not show_secrets and "api-key" in profile_dict:
|
|
632
665
|
key = profile_dict["api-key"]
|
|
633
666
|
profile_dict["api-key"] = "****" + key[-4:] if len(key) >= 4 else "****"
|
|
634
667
|
data["profiles"][name] = profile_dict
|
|
@@ -655,9 +688,23 @@ def register_config_commands(cli: Any) -> None:
|
|
|
655
688
|
rows.append(["Platform", profile.platform or "Unknown"])
|
|
656
689
|
|
|
657
690
|
if profile.auth_mode == "pkce":
|
|
658
|
-
rows.append(
|
|
659
|
-
|
|
691
|
+
rows.append(
|
|
692
|
+
[
|
|
693
|
+
"Authentication",
|
|
694
|
+
f"PKCE (bearer token in {describe_credential_store(profile.credential_store)})",
|
|
695
|
+
]
|
|
696
|
+
)
|
|
660
697
|
if show_secrets:
|
|
698
|
+
rows.append(["PKCE Credentials", _get_profile_secret(profile, "pkce") or ""])
|
|
699
|
+
else:
|
|
700
|
+
if profile.credential_store == "os" and not profile.api_key:
|
|
701
|
+
if show_secrets:
|
|
702
|
+
api_key_display = _get_profile_secret(profile, "api-key") or ""
|
|
703
|
+
else:
|
|
704
|
+
api_key_display = (
|
|
705
|
+
f"stored in {describe_credential_store(profile.credential_store)}"
|
|
706
|
+
)
|
|
707
|
+
elif show_secrets:
|
|
661
708
|
api_key_display = profile.api_key
|
|
662
709
|
else:
|
|
663
710
|
api_key_display = (
|
|
@@ -812,18 +859,56 @@ def register_config_commands(cli: Any) -> None:
|
|
|
812
859
|
)
|
|
813
860
|
click.echo(f"✓ Removed trusted certificate for {origin}")
|
|
814
861
|
|
|
862
|
+
@config.command(name="cleanup")
|
|
863
|
+
def cleanup_pending_credentials() -> None:
|
|
864
|
+
"""Retry pending credential cleanup without deleting profiles."""
|
|
865
|
+
from .utils import check_readonly_mode
|
|
866
|
+
from .credentials import CredentialStoreError
|
|
867
|
+
from .profile_credentials import retry_pending_profile_deletions
|
|
868
|
+
|
|
869
|
+
check_readonly_mode("clean up pending credentials")
|
|
870
|
+
cfg = ProfileConfig.load()
|
|
871
|
+
try:
|
|
872
|
+
completed = retry_pending_profile_deletions(cfg)
|
|
873
|
+
except (CredentialStoreError, RuntimeError) as exc:
|
|
874
|
+
_exit_with_validation_error(
|
|
875
|
+
f"Could not finish pending credential cleanup: {exc}. "
|
|
876
|
+
"Resolve the credential-store issue and retry 'slcli config cleanup'.",
|
|
877
|
+
ExitCodes.GENERAL_ERROR,
|
|
878
|
+
)
|
|
879
|
+
if not completed:
|
|
880
|
+
click.echo("No pending credential cleanup.")
|
|
881
|
+
return
|
|
882
|
+
click.echo(f"✓ Finished pending credential cleanup for: {', '.join(completed)}")
|
|
883
|
+
|
|
815
884
|
@config.command(name="delete")
|
|
816
885
|
@click.argument("name")
|
|
817
886
|
@click.option("--force", "-f", is_flag=True, help="Skip confirmation prompt")
|
|
818
887
|
def delete_profile(name: str, force: bool) -> None:
|
|
819
888
|
"""Delete a profile."""
|
|
820
889
|
from .utils import check_readonly_mode
|
|
890
|
+
from .credentials import CredentialStoreError
|
|
891
|
+
from .profile_credentials import (
|
|
892
|
+
delete_profile_with_credentials,
|
|
893
|
+
retry_pending_profile_deletions,
|
|
894
|
+
)
|
|
821
895
|
|
|
822
896
|
check_readonly_mode("delete a profile")
|
|
823
897
|
|
|
824
898
|
cfg = ProfileConfig.load()
|
|
825
899
|
|
|
900
|
+
try:
|
|
901
|
+
completed = retry_pending_profile_deletions(cfg, name)
|
|
902
|
+
except (CredentialStoreError, RuntimeError) as exc:
|
|
903
|
+
_exit_with_validation_error(
|
|
904
|
+
f"Could not finish deleting profile '{name}': {exc}. Retry when the store is available.",
|
|
905
|
+
ExitCodes.GENERAL_ERROR,
|
|
906
|
+
)
|
|
907
|
+
|
|
826
908
|
if name not in cfg.profiles:
|
|
909
|
+
if completed:
|
|
910
|
+
click.echo(f"✓ Profile '{name}' deleted.")
|
|
911
|
+
return
|
|
827
912
|
click.echo(f"✗ Profile '{name}' not found.", err=True)
|
|
828
913
|
sys.exit(ExitCodes.NOT_FOUND)
|
|
829
914
|
|
|
@@ -835,63 +920,64 @@ def register_config_commands(cli: Any) -> None:
|
|
|
835
920
|
click.echo("Aborted.")
|
|
836
921
|
sys.exit(ExitCodes.GENERAL_ERROR)
|
|
837
922
|
|
|
923
|
+
profile_to_delete = cfg.profiles[name]
|
|
838
924
|
was_current = cfg.current_profile == name
|
|
839
|
-
|
|
840
|
-
|
|
925
|
+
try:
|
|
926
|
+
delete_profile_with_credentials(cfg, profile_to_delete)
|
|
927
|
+
except CredentialStoreError as exc:
|
|
928
|
+
_exit_with_validation_error(
|
|
929
|
+
f"Could not remove stored credentials: {exc}. Deletion is pending; "
|
|
930
|
+
f"retry 'slcli config delete {name} --force'.",
|
|
931
|
+
ExitCodes.GENERAL_ERROR,
|
|
932
|
+
)
|
|
933
|
+
except RuntimeError as exc:
|
|
934
|
+
_exit_with_validation_error(
|
|
935
|
+
f"Could not save profile removal for '{name}': {exc}. "
|
|
936
|
+
"Credentials were not removed or deletion is pending; retry the command.",
|
|
937
|
+
ExitCodes.GENERAL_ERROR,
|
|
938
|
+
)
|
|
841
939
|
|
|
842
940
|
click.echo(f"✓ Profile '{name}' deleted.")
|
|
843
941
|
if was_current and cfg.current_profile:
|
|
844
942
|
click.echo(f" Current profile is now: {cfg.current_profile}")
|
|
845
943
|
|
|
846
|
-
@config.command(name="
|
|
847
|
-
@click.option(
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
)
|
|
853
|
-
@click.option(
|
|
854
|
-
"--delete-keyring",
|
|
855
|
-
is_flag=True,
|
|
856
|
-
help="Delete keyring entries after migration",
|
|
857
|
-
)
|
|
858
|
-
def migrate(profile_name: str, delete_keyring: bool) -> None:
|
|
859
|
-
"""Migrate credentials from keyring to config file.
|
|
944
|
+
@config.command(name="secure")
|
|
945
|
+
@click.option("--profile", "profile_name", help="Profile to secure (defaults to current)")
|
|
946
|
+
@click.option("--all", "secure_all", is_flag=True, help="Secure every file-backed profile")
|
|
947
|
+
def secure_profiles(profile_name: Optional[str], secure_all: bool) -> None:
|
|
948
|
+
"""Move plaintext API keys and PKCE credentials into the OS store."""
|
|
949
|
+
from .utils import check_readonly_mode
|
|
860
950
|
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
from .profiles import migrate_from_keyring
|
|
951
|
+
check_readonly_mode("secure profile credentials")
|
|
952
|
+
if profile_name and secure_all:
|
|
953
|
+
_exit_with_validation_error("Choose either --profile or --all, not both.")
|
|
865
954
|
|
|
866
|
-
# Check if profile already exists
|
|
867
955
|
cfg = ProfileConfig.load()
|
|
868
|
-
if
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
click.echo("✗ No credentials found in keyring.", err=True)
|
|
881
|
-
click.echo("Run 'slcli login --profile <name>' to create a new profile.", err=True)
|
|
882
|
-
sys.exit(ExitCodes.NOT_FOUND)
|
|
956
|
+
if secure_all:
|
|
957
|
+
selected = list(cfg.profiles.values())
|
|
958
|
+
else:
|
|
959
|
+
selected_name = profile_name or cfg.current_profile
|
|
960
|
+
if not selected_name:
|
|
961
|
+
_exit_with_validation_error("No current profile is set.", ExitCodes.NOT_FOUND)
|
|
962
|
+
profile_to_secure = cfg.get_profile(selected_name)
|
|
963
|
+
if not profile_to_secure:
|
|
964
|
+
_exit_with_validation_error(
|
|
965
|
+
f"Profile '{selected_name}' not found.", ExitCodes.NOT_FOUND
|
|
966
|
+
)
|
|
967
|
+
selected = [profile_to_secure]
|
|
883
968
|
|
|
884
|
-
|
|
885
|
-
click.echo(f" Server: {profile.server}")
|
|
886
|
-
if profile.web_url:
|
|
887
|
-
click.echo(f" Web URL: {profile.web_url}")
|
|
888
|
-
if profile.platform:
|
|
889
|
-
click.echo(f" Platform: {profile.platform}")
|
|
969
|
+
from .profile_credentials import ProfileCredentialError, secure_profile_credentials
|
|
890
970
|
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
971
|
+
try:
|
|
972
|
+
secured_names, warnings = secure_profile_credentials(cfg, selected)
|
|
973
|
+
except ProfileCredentialError as exc:
|
|
974
|
+
_exit_with_validation_error(str(exc))
|
|
975
|
+
if not secured_names:
|
|
976
|
+
click.echo("All selected profile credentials are already secured.")
|
|
977
|
+
return
|
|
978
|
+
for warning in warnings:
|
|
979
|
+
click.echo(f"⚠️ {warning}", err=True)
|
|
980
|
+
click.echo(f"✓ Secured credentials for: {', '.join(secured_names)}")
|
|
895
981
|
|
|
896
982
|
@config.command(name="add")
|
|
897
983
|
@click.option("--profile", "-p", help="Profile name (default: 'default')")
|
|
@@ -921,6 +1007,14 @@ def register_config_commands(cli: Any) -> None:
|
|
|
921
1007
|
"(defaults to openid profile email offline_access)"
|
|
922
1008
|
),
|
|
923
1009
|
)
|
|
1010
|
+
@click.option(
|
|
1011
|
+
"--credential-store",
|
|
1012
|
+
type=click.Choice(["os", "file"]),
|
|
1013
|
+
default="os",
|
|
1014
|
+
envvar="SLCLI_CREDENTIAL_STORE",
|
|
1015
|
+
show_default=True,
|
|
1016
|
+
help="Store credentials in the OS store or config file",
|
|
1017
|
+
)
|
|
924
1018
|
@click.option("--workspace", "-w", help="Default workspace for this profile")
|
|
925
1019
|
@click.option(
|
|
926
1020
|
"--set-current/--no-set-current",
|
|
@@ -948,6 +1042,7 @@ def register_config_commands(cli: Any) -> None:
|
|
|
948
1042
|
client_id: Optional[str],
|
|
949
1043
|
callback_port: Optional[int],
|
|
950
1044
|
scopes: tuple[str, ...],
|
|
1045
|
+
credential_store: str,
|
|
951
1046
|
workspace: Optional[str],
|
|
952
1047
|
set_current: bool,
|
|
953
1048
|
readonly: bool,
|
|
@@ -956,7 +1051,7 @@ def register_config_commands(cli: Any) -> None:
|
|
|
956
1051
|
"""Add or update a SystemLink profile.
|
|
957
1052
|
|
|
958
1053
|
Profiles allow you to configure multiple SystemLink environments and switch
|
|
959
|
-
between them. Credentials
|
|
1054
|
+
between them. Credentials use the OS store by default.
|
|
960
1055
|
|
|
961
1056
|
The readonly flag enables readonly mode, which disables all delete and edit
|
|
962
1057
|
commands in slcli. This is useful for AI agents or untrusted environments.
|
|
@@ -975,6 +1070,7 @@ def register_config_commands(cli: Any) -> None:
|
|
|
975
1070
|
client_id=client_id,
|
|
976
1071
|
callback_port=callback_port,
|
|
977
1072
|
scopes=scopes,
|
|
1073
|
+
credential_store=credential_store,
|
|
978
1074
|
workspace=workspace,
|
|
979
1075
|
set_current=set_current,
|
|
980
1076
|
readonly=readonly,
|