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.
Files changed (189) hide show
  1. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/PKG-INFO +7 -1
  2. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/pyproject.toml +10 -3
  3. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/_version.py +1 -1
  4. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/config_click.py +189 -93
  5. systemlink_cli-2.2.2/slcli/credentials.py +511 -0
  6. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/function_click.py +1 -1
  7. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/main.py +98 -80
  8. systemlink_cli-2.2.2/slcli/managed_client/__init__.py +63 -0
  9. systemlink_cli-2.2.2/slcli/managed_client/crypto.py +433 -0
  10. systemlink_cli-2.2.2/slcli/managed_client/handlers.py +415 -0
  11. systemlink_cli-2.2.2/slcli/managed_client/minion.py +543 -0
  12. systemlink_cli-2.2.2/slcli/managed_client/models.py +100 -0
  13. systemlink_cli-2.2.2/slcli/managed_client/protocol.py +485 -0
  14. systemlink_cli-2.2.2/slcli/managed_client/rest.py +273 -0
  15. systemlink_cli-2.2.2/slcli/managed_client/state.py +328 -0
  16. systemlink_cli-2.2.2/slcli/managed_client/transport.py +139 -0
  17. systemlink_cli-2.2.2/slcli/managed_client_click.py +320 -0
  18. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/pkce.py +112 -102
  19. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/platform.py +35 -50
  20. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/policy_click.py +158 -1
  21. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/policy_utils.py +17 -5
  22. systemlink_cli-2.2.2/slcli/profile_credentials.py +444 -0
  23. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/profiles.py +79 -118
  24. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/rich_output.py +87 -19
  25. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/skills/slcli/references/commands.md +27 -4
  26. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/utils.py +114 -128
  27. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/LICENSE +0 -0
  28. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/dff-editor/editor.js +0 -0
  29. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/dff-editor/index.html +0 -0
  30. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/__init__.py +0 -0
  31. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/__main__.py +0 -0
  32. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/alarm_click.py +0 -0
  33. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/asset_click.py +0 -0
  34. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/cli_formatters.py +0 -0
  35. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/cli_utils.py +0 -0
  36. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/comment_click.py +0 -0
  37. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/completion_click.py +0 -0
  38. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/config.py +0 -0
  39. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/dataframe_click.py +0 -0
  40. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/dff_click.py +0 -0
  41. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/dff_decorators.py +0 -0
  42. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/example_click.py +0 -0
  43. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/example_loader.py +0 -0
  44. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/example_provisioner.py +0 -0
  45. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/examples/README.md +0 -0
  46. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/examples/_schema/schema-v1.0.json +0 -0
  47. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/examples/demo-data-1/README.md +0 -0
  48. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/examples/demo-data-1/config.yaml +0 -0
  49. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/examples/demo-data-2/README.md +0 -0
  50. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/examples/demo-data-2/config.yaml +0 -0
  51. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/examples/demo-data-3/config.yaml +0 -0
  52. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/examples/demo-data-3/leakage-current-results.json +0 -0
  53. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/examples/demo-data-3/overvoltage-results.json +0 -0
  54. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/examples/demo-data-3/product-xyz-compliance-report.json +0 -0
  55. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/examples/demo-data-3/product-xyz-specification.csv +0 -0
  56. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/examples/demo-data-3/thermal-response-results.json +0 -0
  57. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/examples/exercise-5-1-parametric-insights/README.md +0 -0
  58. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/examples/exercise-5-1-parametric-insights/config.yaml +0 -0
  59. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/examples/exercise-7-1-test-plans/README.md +0 -0
  60. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/examples/exercise-7-1-test-plans/config.yaml +0 -0
  61. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/examples/spec-compliance-notebooks/README.md +0 -0
  62. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/examples/spec-compliance-notebooks/config.yaml +0 -0
  63. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/examples/spec-compliance-notebooks/notebooks/SpecAnalysis_ComplianceCalculation.ipynb +0 -0
  64. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/examples/spec-compliance-notebooks/notebooks/SpecComplianceCalculation.ipynb +0 -0
  65. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/examples/spec-compliance-notebooks/notebooks/SpecfileExtractionAndIngestion.ipynb +0 -0
  66. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/examples/spec-compliance-notebooks/spec_template.xlsx +0 -0
  67. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/feed_click.py +0 -0
  68. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/file_click.py +0 -0
  69. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/function_templates.py +0 -0
  70. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/mcp_click.py +0 -0
  71. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/mcp_reachability.py +0 -0
  72. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/mcp_server.py +0 -0
  73. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/mcp_skills.py +0 -0
  74. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/notebook_click.py +0 -0
  75. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/response_handlers.py +0 -0
  76. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/routine_click.py +0 -0
  77. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/skill_click.py +0 -0
  78. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/skills/slcli/SKILL.md +0 -0
  79. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/skills/slcli/evals/COPILOT_BATCH_RUN_PROMPT.md +0 -0
  80. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/skills/slcli/evals/README.md +0 -0
  81. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/skills/slcli/evals/evals.json +0 -0
  82. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/skills/slcli/evals/evals.schema.json +0 -0
  83. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/skills/slcli/evals/files/spec-import/battery-specs.csv +0 -0
  84. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/skills/slcli/evals/files/webapp-package/icon.svg +0 -0
  85. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/skills/slcli/evals/files/webapp-package/nipkg.config.json +0 -0
  86. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/skills/slcli/evals/files/webapp-package/webapp-content/index.html +0 -0
  87. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/skills/slcli/evals/trigger_evals.json +0 -0
  88. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/skills/slcli/references/analysis-recipes.md +0 -0
  89. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/skills/slcli/references/datasheet-workflow.md +0 -0
  90. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/skills/slcli/references/domain-concepts.md +0 -0
  91. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/skills/slcli/references/example-authoring.md +0 -0
  92. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/skills/slcli/references/filtering.md +0 -0
  93. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/skills/slcli/references/import-specs.min.json +0 -0
  94. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/skills/slcli/references/job-debugging/overview.md +0 -0
  95. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/skills/slcli/references/nipkg/overview.md +0 -0
  96. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/skills/slcli/references/notebook/interfaces.md +0 -0
  97. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/skills/slcli/references/notebook/notebook-patterns.md +0 -0
  98. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/skills/slcli/references/notebook/overview.md +0 -0
  99. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/skills/slcli/references/python-test/overview.md +0 -0
  100. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/skills/slcli/references/routine-examples.md +0 -0
  101. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/skills/slcli/references/troubleshooting.md +0 -0
  102. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/skills/slcli/references/webapp/angular-ui-packages.md +0 -0
  103. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/skills/slcli/references/webapp/deployment.md +0 -0
  104. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/skills/slcli/references/webapp/layout-patterns.md +0 -0
  105. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/skills/slcli/references/webapp/nimble-angular.md +0 -0
  106. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/skills/slcli/references/webapp/overview.md +0 -0
  107. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/skills/slcli/references/webapp/systemlink-services.md +0 -0
  108. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/skills/slcli/references/webapp/troubleshooting.md +0 -0
  109. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/skills/slcli/scripts/benchmark_iteration.py +0 -0
  110. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/skills/slcli/scripts/compare_iteration.py +0 -0
  111. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/skills/slcli/scripts/eval_manifest.py +0 -0
  112. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/skills/slcli/scripts/grade_eval_response.py +0 -0
  113. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/skills/slcli/scripts/grade_iteration.py +0 -0
  114. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/skills/slcli/scripts/prepare_eval_prompts.py +0 -0
  115. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/skills/slcli/scripts/prepare_eval_workspace.py +0 -0
  116. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/skills/slcli/scripts/render_eval_review.py +0 -0
  117. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/skills/slcli/scripts/spec_import_helper.py +0 -0
  118. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/spec_click.py +0 -0
  119. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/ssl_trust.py +0 -0
  120. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/state_click.py +0 -0
  121. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/system_click.py +0 -0
  122. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/system_query_utils.py +0 -0
  123. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/systemlink-logo.svg +0 -0
  124. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/table_utils.py +0 -0
  125. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/tag_click.py +0 -0
  126. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/templates_click.py +0 -0
  127. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/testmonitor_click.py +0 -0
  128. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/universal_handlers.py +0 -0
  129. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/user_click.py +0 -0
  130. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/version_click.py +0 -0
  131. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/web_editor.py +0 -0
  132. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/webapp_bootstrap.py +0 -0
  133. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/webapp_click.py +0 -0
  134. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/webapp_templates/angular/blank/.gitignore +0 -0
  135. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/webapp_templates/angular/blank/README.md +0 -0
  136. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/webapp_templates/angular/blank/angular.json +0 -0
  137. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/webapp_templates/angular/blank/package.json +0 -0
  138. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/webapp_templates/angular/blank/scripts/filter-sbom.js +0 -0
  139. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/webapp_templates/angular/blank/src/app/app-routing.module.ts +0 -0
  140. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/webapp_templates/angular/blank/src/app/app.component.html +0 -0
  141. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/webapp_templates/angular/blank/src/app/app.component.scss +0 -0
  142. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/webapp_templates/angular/blank/src/app/app.component.ts +0 -0
  143. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/webapp_templates/angular/blank/src/app/app.module.ts +0 -0
  144. {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
  145. {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
  146. {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
  147. {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
  148. {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
  149. {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
  150. {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
  151. {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
  152. {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
  153. {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
  154. {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
  155. {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
  156. {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
  157. {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
  158. {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
  159. {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
  160. {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
  161. {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
  162. {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
  163. {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
  164. {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
  165. {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
  166. {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
  167. {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
  168. {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
  169. {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
  170. {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
  171. {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
  172. {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
  173. {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
  174. {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
  175. {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
  176. {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
  177. {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
  178. {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
  179. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/webapp_templates/angular/blank/src/assets/README.md +0 -0
  180. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/webapp_templates/angular/blank/src/index.html +0 -0
  181. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/webapp_templates/angular/blank/src/main.ts +0 -0
  182. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/webapp_templates/angular/blank/src/styles.scss +0 -0
  183. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/webapp_templates/angular/blank/tsconfig.app.json +0 -0
  184. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/webapp_templates/angular/blank/tsconfig.json +0 -0
  185. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/workflow_preview.py +0 -0
  186. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/workflows_click.py +0 -0
  187. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/workitem_click.py +0 -0
  188. {systemlink_cli-1.30.3 → systemlink_cli-2.2.2}/slcli/workspace_click.py +0 -0
  189. {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: 1.30.3
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 = "1.30.3"
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
- mcp = ["mcp"]
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 = "^25.8.0"
85
+ towncrier = "^26.0.0"
79
86
  pytest-xdist = "^3.8.0"
80
87
 
81
88
  # Excel file generation
@@ -1,4 +1,4 @@
1
1
  """Version information for slcli."""
2
2
 
3
3
  # This file is auto-generated. Do not edit manually.
4
- __version__ = "1.30.3"
4
+ __version__ = "2.2.2"
@@ -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 ProfileConfig, Profile, check_config_file_permissions
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", "SYSTEMLINK_API_URL")),
37
- ("API Key", ("SLCLI_API_KEY", "SYSTEMLINK_API_KEY")),
38
- ("Web URL", ("SLCLI_WEB_URL", "SYSTEMLINK_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
- from .pkce import save_pkce_credentials
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
- try:
437
- save_pkce_credentials(
438
- profile,
439
- pkce_result.access_token,
440
- pkce_result.refresh_token,
441
- pkce_result.expires_at,
442
- )
443
- except Exception as exc:
444
- if previous_profile is None:
445
- cfg.profiles.pop(profile, None)
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
- cfg = ProfileConfig.load()
586
-
587
- if name not in cfg.profiles:
588
- click.echo(f"✗ Profile '{name}' not found.", err=True)
589
- if cfg.profiles:
590
- click.echo(f"Available profiles: {', '.join(cfg.profiles.keys())}", err=True)
591
- sys.exit(ExitCodes.NOT_FOUND)
592
-
593
- cfg.set_current_profile(name)
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 not show_secrets and "api-key" in profile_dict:
631
- # Show only last 4 characters
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(["Authentication", "PKCE (bearer token in keyring)"])
659
- else:
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
- cfg.delete_profile(name)
840
- cfg.save()
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="migrate")
847
- @click.option(
848
- "--profile-name",
849
- "-n",
850
- default="default",
851
- help="Name for the migrated profile",
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
- This command reads existing credentials from the system keyring
862
- and creates a new profile in the config file.
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 profile_name in cfg.profiles:
869
- if not questionary.confirm(
870
- f"Profile '{profile_name}' already exists. Overwrite?",
871
- default=False,
872
- ).ask():
873
- click.echo("Aborted.")
874
- sys.exit(ExitCodes.GENERAL_ERROR)
875
-
876
- # Use centralized migration function
877
- profile = migrate_from_keyring(profile_name=profile_name, delete_keyring=delete_keyring)
878
-
879
- if not profile:
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
- click.echo(f"✓ Migrated credentials to profile '{profile_name}'")
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
- if delete_keyring:
892
- click.echo("✓ Deleted keyring entries")
893
- else:
894
- click.echo("\nNote: Keyring entries still exist. Use --delete-keyring to remove them.")
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 are stored in ~/.config/slcli/config.json.
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,