cycode 3.13.0__tar.gz → 3.13.1.dev1__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 (209) hide show
  1. {cycode-3.13.0 → cycode-3.13.1.dev1}/PKG-INFO +64 -2
  2. {cycode-3.13.0 → cycode-3.13.1.dev1}/README.md +63 -1
  3. cycode-3.13.1.dev1/cycode/__init__.py +1 -0
  4. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/app.py +23 -0
  5. cycode-3.13.1.dev1/cycode/cli/apps/api/__init__.py +69 -0
  6. cycode-3.13.1.dev1/cycode/cli/apps/api/api_command.py +271 -0
  7. cycode-3.13.1.dev1/cycode/cli/apps/api/openapi_spec.py +182 -0
  8. {cycode-3.13.0 → cycode-3.13.1.dev1}/pyproject.toml +1 -1
  9. cycode-3.13.0/cycode/__init__.py +0 -1
  10. {cycode-3.13.0 → cycode-3.13.1.dev1}/LICENCE +0 -0
  11. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/__main__.py +0 -0
  12. {cycode-3.13.0/cycode/cyclient → cycode-3.13.1.dev1/cycode/cli}/__init__.py +0 -0
  13. {cycode-3.13.0/cycode/cli/utils → cycode-3.13.1.dev1/cycode/cli/apps}/__init__.py +0 -0
  14. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/activation_manager.py +0 -0
  15. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/ai_guardrails/__init__.py +0 -0
  16. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/ai_guardrails/command_utils.py +0 -0
  17. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/ai_guardrails/consts.py +0 -0
  18. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/ai_guardrails/ensure_auth_command.py +0 -0
  19. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/ai_guardrails/hooks_manager.py +0 -0
  20. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/ai_guardrails/install_command.py +0 -0
  21. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/ai_guardrails/scan/__init__.py +0 -0
  22. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/ai_guardrails/scan/claude_config.py +0 -0
  23. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/ai_guardrails/scan/consts.py +0 -0
  24. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/ai_guardrails/scan/handlers.py +0 -0
  25. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/ai_guardrails/scan/payload.py +0 -0
  26. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/ai_guardrails/scan/policy.py +0 -0
  27. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/ai_guardrails/scan/response_builders.py +0 -0
  28. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/ai_guardrails/scan/scan_command.py +0 -0
  29. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/ai_guardrails/scan/types.py +0 -0
  30. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/ai_guardrails/scan/utils.py +0 -0
  31. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/ai_guardrails/status_command.py +0 -0
  32. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/ai_guardrails/uninstall_command.py +0 -0
  33. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/ai_remediation/__init__.py +0 -0
  34. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/ai_remediation/ai_remediation_command.py +0 -0
  35. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/ai_remediation/apply_fix.py +0 -0
  36. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/ai_remediation/print_remediation.py +0 -0
  37. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/auth/__init__.py +0 -0
  38. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/auth/auth_command.py +0 -0
  39. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/auth/auth_common.py +0 -0
  40. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/auth/auth_manager.py +0 -0
  41. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/auth/models.py +0 -0
  42. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/configure/__init__.py +0 -0
  43. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/configure/configure_command.py +0 -0
  44. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/configure/consts.py +0 -0
  45. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/configure/messages.py +0 -0
  46. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/configure/prompts.py +0 -0
  47. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/ignore/__init__.py +0 -0
  48. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/ignore/ignore_command.py +0 -0
  49. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/mcp/__init__.py +0 -0
  50. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/mcp/mcp_command.py +0 -0
  51. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/report/__init__.py +0 -0
  52. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/report/report_command.py +0 -0
  53. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/report/sbom/__init__.py +0 -0
  54. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/report/sbom/common.py +0 -0
  55. {cycode-3.13.0/cycode/cli/user_settings → cycode-3.13.1.dev1/cycode/cli/apps/report/sbom/path}/__init__.py +0 -0
  56. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/report/sbom/path/path_command.py +0 -0
  57. {cycode-3.13.0/cycode/cli/printers/utils/detection_ordering → cycode-3.13.1.dev1/cycode/cli/apps/report/sbom/repository_url}/__init__.py +0 -0
  58. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/report/sbom/repository_url/repository_url_command.py +0 -0
  59. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/report/sbom/sbom_command.py +0 -0
  60. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/report/sbom/sbom_report_file.py +0 -0
  61. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/report_import/__init__.py +0 -0
  62. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/report_import/report_import_command.py +0 -0
  63. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/report_import/sbom/__init__.py +0 -0
  64. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/report_import/sbom/sbom_command.py +0 -0
  65. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/sca_options.py +0 -0
  66. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/scan/__init__.py +0 -0
  67. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/scan/aggregation_report.py +0 -0
  68. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/scan/code_scanner.py +0 -0
  69. {cycode-3.13.0/cycode/cli/printers/tables → cycode-3.13.1.dev1/cycode/cli/apps/scan/commit_history}/__init__.py +0 -0
  70. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/scan/commit_history/commit_history_command.py +0 -0
  71. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/scan/commit_range_scanner.py +0 -0
  72. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/scan/detection_excluder.py +0 -0
  73. {cycode-3.13.0/cycode/cli/files_collector/sca/sbt → cycode-3.13.1.dev1/cycode/cli/apps/scan/path}/__init__.py +0 -0
  74. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/scan/path/path_command.py +0 -0
  75. {cycode-3.13.0/cycode/cli/files_collector/sca/ruby → cycode-3.13.1.dev1/cycode/cli/apps/scan/pre_commit}/__init__.py +0 -0
  76. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/scan/pre_commit/pre_commit_command.py +0 -0
  77. {cycode-3.13.0/cycode/cli/files_collector/sca/python → cycode-3.13.1.dev1/cycode/cli/apps/scan/pre_push}/__init__.py +0 -0
  78. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/scan/pre_push/pre_push_command.py +0 -0
  79. {cycode-3.13.0/cycode/cli/files_collector/sca/php → cycode-3.13.1.dev1/cycode/cli/apps/scan/pre_receive}/__init__.py +0 -0
  80. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/scan/pre_receive/pre_receive_command.py +0 -0
  81. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/scan/remote_url_resolver.py +0 -0
  82. {cycode-3.13.0/cycode/cli/files_collector/sca/nuget → cycode-3.13.1.dev1/cycode/cli/apps/scan/repository}/__init__.py +0 -0
  83. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/scan/repository/repository_command.py +0 -0
  84. {cycode-3.13.0/cycode/cli/files_collector/sca/npm → cycode-3.13.1.dev1/cycode/cli/apps/scan/scan_ci}/__init__.py +0 -0
  85. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/scan/scan_ci/ci_integrations.py +0 -0
  86. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/scan/scan_ci/scan_ci_command.py +0 -0
  87. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/scan/scan_command.py +0 -0
  88. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/scan/scan_parameters.py +0 -0
  89. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/scan/scan_result.py +0 -0
  90. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/status/__init__.py +0 -0
  91. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/status/get_cli_status.py +0 -0
  92. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/status/models.py +0 -0
  93. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/status/status_command.py +0 -0
  94. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/status/version_command.py +0 -0
  95. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/cli_types.py +0 -0
  96. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/config.py +0 -0
  97. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/console.py +0 -0
  98. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/consts.py +0 -0
  99. {cycode-3.13.0/cycode/cli/files_collector/sca/maven → cycode-3.13.1.dev1/cycode/cli/exceptions}/__init__.py +0 -0
  100. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/exceptions/custom_exceptions.py +0 -0
  101. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/exceptions/handle_ai_remediation_errors.py +0 -0
  102. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/exceptions/handle_auth_errors.py +0 -0
  103. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/exceptions/handle_errors.py +0 -0
  104. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/exceptions/handle_report_sbom_errors.py +0 -0
  105. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/exceptions/handle_scan_errors.py +0 -0
  106. {cycode-3.13.0/cycode/cli/files_collector/sca/go → cycode-3.13.1.dev1/cycode/cli/files_collector}/__init__.py +0 -0
  107. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/files_collector/commit_range_documents.py +0 -0
  108. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/files_collector/documents_walk_ignore.py +0 -0
  109. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/files_collector/file_excluder.py +0 -0
  110. {cycode-3.13.0/cycode/cli/files_collector/sca → cycode-3.13.1.dev1/cycode/cli/files_collector/iac}/__init__.py +0 -0
  111. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/files_collector/iac/tf_content_generator.py +0 -0
  112. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/files_collector/models/__init__.py +0 -0
  113. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/files_collector/models/in_memory_zip.py +0 -0
  114. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/files_collector/path_documents.py +0 -0
  115. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/files_collector/repository_documents.py +0 -0
  116. {cycode-3.13.0/cycode/cli/files_collector/iac → cycode-3.13.1.dev1/cycode/cli/files_collector/sca}/__init__.py +0 -0
  117. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/files_collector/sca/base_restore_dependencies.py +0 -0
  118. {cycode-3.13.0/cycode/cli/files_collector → cycode-3.13.1.dev1/cycode/cli/files_collector/sca/go}/__init__.py +0 -0
  119. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/files_collector/sca/go/restore_go_dependencies.py +0 -0
  120. {cycode-3.13.0/cycode/cli/exceptions → cycode-3.13.1.dev1/cycode/cli/files_collector/sca/maven}/__init__.py +0 -0
  121. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/files_collector/sca/maven/restore_gradle_dependencies.py +0 -0
  122. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/files_collector/sca/maven/restore_maven_dependencies.py +0 -0
  123. {cycode-3.13.0/cycode/cli/apps/scan/scan_ci → cycode-3.13.1.dev1/cycode/cli/files_collector/sca/npm}/__init__.py +0 -0
  124. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/files_collector/sca/npm/restore_deno_dependencies.py +0 -0
  125. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/files_collector/sca/npm/restore_npm_dependencies.py +0 -0
  126. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/files_collector/sca/npm/restore_pnpm_dependencies.py +0 -0
  127. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/files_collector/sca/npm/restore_yarn_dependencies.py +0 -0
  128. {cycode-3.13.0/cycode/cli/apps/scan/repository → cycode-3.13.1.dev1/cycode/cli/files_collector/sca/nuget}/__init__.py +0 -0
  129. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/files_collector/sca/nuget/restore_nuget_dependencies.py +0 -0
  130. {cycode-3.13.0/cycode/cli/apps/scan/pre_receive → cycode-3.13.1.dev1/cycode/cli/files_collector/sca/php}/__init__.py +0 -0
  131. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/files_collector/sca/php/restore_composer_dependencies.py +0 -0
  132. {cycode-3.13.0/cycode/cli/apps/scan/pre_push → cycode-3.13.1.dev1/cycode/cli/files_collector/sca/python}/__init__.py +0 -0
  133. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/files_collector/sca/python/restore_pipenv_dependencies.py +0 -0
  134. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/files_collector/sca/python/restore_poetry_dependencies.py +0 -0
  135. {cycode-3.13.0/cycode/cli/apps/scan/pre_commit → cycode-3.13.1.dev1/cycode/cli/files_collector/sca/ruby}/__init__.py +0 -0
  136. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/files_collector/sca/ruby/restore_ruby_dependencies.py +0 -0
  137. {cycode-3.13.0/cycode/cli/apps/scan/path → cycode-3.13.1.dev1/cycode/cli/files_collector/sca/sbt}/__init__.py +0 -0
  138. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/files_collector/sca/sbt/restore_sbt_dependencies.py +0 -0
  139. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/files_collector/sca/sca_file_collector.py +0 -0
  140. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/files_collector/walk_ignore.py +0 -0
  141. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/files_collector/zip_documents.py +0 -0
  142. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/logger.py +0 -0
  143. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/main.py +0 -0
  144. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/models.py +0 -0
  145. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/printers/__init__.py +0 -0
  146. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/printers/console_printer.py +0 -0
  147. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/printers/json_printer.py +0 -0
  148. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/printers/printer_base.py +0 -0
  149. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/printers/rich_printer.py +0 -0
  150. {cycode-3.13.0/cycode/cli/apps/scan/commit_history → cycode-3.13.1.dev1/cycode/cli/printers/tables}/__init__.py +0 -0
  151. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/printers/tables/sca_table_printer.py +0 -0
  152. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/printers/tables/table.py +0 -0
  153. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/printers/tables/table_models.py +0 -0
  154. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/printers/tables/table_printer.py +0 -0
  155. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/printers/tables/table_printer_base.py +0 -0
  156. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/printers/text_printer.py +0 -0
  157. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/printers/utils/__init__.py +0 -0
  158. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/printers/utils/code_snippet_syntax.py +0 -0
  159. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/printers/utils/detection_data.py +0 -0
  160. {cycode-3.13.0/cycode/cli/apps/report/sbom/repository_url → cycode-3.13.1.dev1/cycode/cli/printers/utils/detection_ordering}/__init__.py +0 -0
  161. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/printers/utils/detection_ordering/common_ordering.py +0 -0
  162. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/printers/utils/detection_ordering/sca_ordering.py +0 -0
  163. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/printers/utils/rich_helpers.py +0 -0
  164. {cycode-3.13.0/cycode/cli/apps/report/sbom/path → cycode-3.13.1.dev1/cycode/cli/user_settings}/__init__.py +0 -0
  165. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/user_settings/base_file_manager.py +0 -0
  166. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/user_settings/config_file_manager.py +0 -0
  167. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/user_settings/configuration_manager.py +0 -0
  168. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/user_settings/credentials_manager.py +0 -0
  169. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/user_settings/jwt_creator.py +0 -0
  170. {cycode-3.13.0/cycode/cli/apps → cycode-3.13.1.dev1/cycode/cli/utils}/__init__.py +0 -0
  171. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/utils/binary_utils.py +0 -0
  172. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/utils/enum_utils.py +0 -0
  173. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/utils/get_api_client.py +0 -0
  174. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/utils/git_proxy.py +0 -0
  175. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/utils/ignore_utils.py +0 -0
  176. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/utils/jwt_utils.py +0 -0
  177. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/utils/path_utils.py +0 -0
  178. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/utils/progress_bar.py +0 -0
  179. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/utils/scan_batch.py +0 -0
  180. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/utils/scan_utils.py +0 -0
  181. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/utils/shell_executor.py +0 -0
  182. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/utils/string_utils.py +0 -0
  183. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/utils/task_timer.py +0 -0
  184. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/utils/url_utils.py +0 -0
  185. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/utils/version_checker.py +0 -0
  186. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/utils/yaml_utils.py +0 -0
  187. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/config.py +0 -0
  188. {cycode-3.13.0/cycode/cli → cycode-3.13.1.dev1/cycode/cyclient}/__init__.py +0 -0
  189. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cyclient/ai_security_manager_client.py +0 -0
  190. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cyclient/ai_security_manager_service_config.py +0 -0
  191. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cyclient/auth_client.py +0 -0
  192. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cyclient/base_token_auth_client.py +0 -0
  193. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cyclient/cli_activation_client.py +0 -0
  194. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cyclient/client_creator.py +0 -0
  195. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cyclient/config.py +0 -0
  196. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cyclient/config_dev.py +0 -0
  197. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cyclient/cycode_client.py +0 -0
  198. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cyclient/cycode_client_base.py +0 -0
  199. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cyclient/cycode_dev_based_client.py +0 -0
  200. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cyclient/cycode_oidc_based_client.py +0 -0
  201. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cyclient/cycode_token_based_client.py +0 -0
  202. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cyclient/headers.py +0 -0
  203. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cyclient/import_sbom_client.py +0 -0
  204. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cyclient/logger.py +0 -0
  205. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cyclient/models.py +0 -0
  206. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cyclient/report_client.py +0 -0
  207. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cyclient/scan_client.py +0 -0
  208. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cyclient/scan_config_base.py +0 -0
  209. {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/logger.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cycode
3
- Version: 3.13.0
3
+ Version: 3.13.1.dev1
4
4
  Summary: Boost security in your dev lifecycle via SAST, SCA, Secrets & IaC scanning.
5
5
  License-Expression: MIT
6
6
  License-File: LICENCE
@@ -62,7 +62,11 @@ This guide walks you through both installation and usage.
62
62
  2. [Available Options](#available-options)
63
63
  3. [MCP Tools](#mcp-tools)
64
64
  4. [Usage Examples](#usage-examples)
65
- 5. [Scan Command](#scan-command)
65
+ 5. [Platform Command](#platform-command-beta)
66
+ 1. [Discovering Commands](#discovering-commands)
67
+ 2. [Examples](#platform-examples)
68
+ 3. [Notes & Limitations](#platform-notes--limitations)
69
+ 6. [Scan Command](#scan-command)
66
70
  1. [Running a Scan](#running-a-scan)
67
71
  1. [Options](#options)
68
72
  1. [Severity Threshold](#severity-option)
@@ -646,6 +650,64 @@ This information can be helpful when:
646
650
  - Debugging transport-specific issues
647
651
 
648
652
 
653
+ # Platform Command \[BETA\]
654
+
655
+ > [!WARNING]
656
+ > The `platform` command is in **beta**. Commands, arguments, and output formats are generated dynamically from the Cycode API spec and may change between releases without notice. Do not rely on them in production automation yet.
657
+
658
+ The `cycode platform` command exposes the Cycode platform's read APIs as CLI commands. It groups endpoints by resource (e.g. `projects`, `violations`, `workflows`) and turns each endpoint's parameters into typed CLI arguments and `--option` flags.
659
+
660
+ ```bash
661
+ cycode platform projects list --page-size 50
662
+ cycode platform violations count
663
+ cycode platform workflows view <workflow-id>
664
+ ```
665
+
666
+ The OpenAPI spec is fetched from the Cycode API on first use and cached at `~/.cycode/openapi-spec.json` for 24 hours. Unrelated commands (`cycode scan`, `cycode status`, etc.) do not trigger a fetch.
667
+
668
+ > [!NOTE]
669
+ > You must be authenticated (`cycode auth` or `CYCODE_CLIENT_ID` / `CYCODE_CLIENT_SECRET` environment variables) for `cycode platform` to discover and run commands. Other Cycode CLI commands work without authentication.
670
+
671
+ ## Discovering Commands
672
+
673
+ Because commands are generated from the spec, the source of truth for what's available is `--help`:
674
+
675
+ ```bash
676
+ cycode platform --help # list all resource groups
677
+ cycode platform projects --help # list actions on a resource
678
+ cycode platform projects list --help # list options/arguments for an action
679
+ ```
680
+
681
+ ## Platform Examples
682
+
683
+ ```bash
684
+ # List projects with pagination
685
+ cycode platform projects list --page-size 25
686
+
687
+ # View a single project by ID
688
+ cycode platform projects view <project-id>
689
+
690
+ # Count violations across the tenant
691
+ cycode platform violations count
692
+
693
+ # Filter using query parameters (see `--help` for what each endpoint supports)
694
+ cycode platform violations list --severity CRITICAL
695
+ ```
696
+
697
+ All output is JSON by default — pipe it through `jq` for ad-hoc filtering:
698
+
699
+ ```bash
700
+ cycode platform projects list --page-size 100 | jq '.items[].name'
701
+ ```
702
+
703
+ ## Platform Notes & Limitations
704
+
705
+ - **Read-only today.** Only `GET` endpoints are exposed in this beta.
706
+ - **Spec-driven.** Adding a new endpoint to the API surfaces it automatically the next time the cache is refreshed.
707
+ - **No bundled spec.** The first `cycode platform` invocation after install (or after the 24h cache expires) performs a network fetch. On slow connections this first call may take a few seconds; subsequent calls are near-instant until the cache expires.
708
+ - **Override the cache TTL** with `CYCODE_SPEC_CACHE_TTL=<seconds>`.
709
+
710
+
649
711
  # Scan Command
650
712
 
651
713
  ## Running a Scan
@@ -21,7 +21,11 @@ This guide walks you through both installation and usage.
21
21
  2. [Available Options](#available-options)
22
22
  3. [MCP Tools](#mcp-tools)
23
23
  4. [Usage Examples](#usage-examples)
24
- 5. [Scan Command](#scan-command)
24
+ 5. [Platform Command](#platform-command-beta)
25
+ 1. [Discovering Commands](#discovering-commands)
26
+ 2. [Examples](#platform-examples)
27
+ 3. [Notes & Limitations](#platform-notes--limitations)
28
+ 6. [Scan Command](#scan-command)
25
29
  1. [Running a Scan](#running-a-scan)
26
30
  1. [Options](#options)
27
31
  1. [Severity Threshold](#severity-option)
@@ -605,6 +609,64 @@ This information can be helpful when:
605
609
  - Debugging transport-specific issues
606
610
 
607
611
 
612
+ # Platform Command \[BETA\]
613
+
614
+ > [!WARNING]
615
+ > The `platform` command is in **beta**. Commands, arguments, and output formats are generated dynamically from the Cycode API spec and may change between releases without notice. Do not rely on them in production automation yet.
616
+
617
+ The `cycode platform` command exposes the Cycode platform's read APIs as CLI commands. It groups endpoints by resource (e.g. `projects`, `violations`, `workflows`) and turns each endpoint's parameters into typed CLI arguments and `--option` flags.
618
+
619
+ ```bash
620
+ cycode platform projects list --page-size 50
621
+ cycode platform violations count
622
+ cycode platform workflows view <workflow-id>
623
+ ```
624
+
625
+ The OpenAPI spec is fetched from the Cycode API on first use and cached at `~/.cycode/openapi-spec.json` for 24 hours. Unrelated commands (`cycode scan`, `cycode status`, etc.) do not trigger a fetch.
626
+
627
+ > [!NOTE]
628
+ > You must be authenticated (`cycode auth` or `CYCODE_CLIENT_ID` / `CYCODE_CLIENT_SECRET` environment variables) for `cycode platform` to discover and run commands. Other Cycode CLI commands work without authentication.
629
+
630
+ ## Discovering Commands
631
+
632
+ Because commands are generated from the spec, the source of truth for what's available is `--help`:
633
+
634
+ ```bash
635
+ cycode platform --help # list all resource groups
636
+ cycode platform projects --help # list actions on a resource
637
+ cycode platform projects list --help # list options/arguments for an action
638
+ ```
639
+
640
+ ## Platform Examples
641
+
642
+ ```bash
643
+ # List projects with pagination
644
+ cycode platform projects list --page-size 25
645
+
646
+ # View a single project by ID
647
+ cycode platform projects view <project-id>
648
+
649
+ # Count violations across the tenant
650
+ cycode platform violations count
651
+
652
+ # Filter using query parameters (see `--help` for what each endpoint supports)
653
+ cycode platform violations list --severity CRITICAL
654
+ ```
655
+
656
+ All output is JSON by default — pipe it through `jq` for ad-hoc filtering:
657
+
658
+ ```bash
659
+ cycode platform projects list --page-size 100 | jq '.items[].name'
660
+ ```
661
+
662
+ ## Platform Notes & Limitations
663
+
664
+ - **Read-only today.** Only `GET` endpoints are exposed in this beta.
665
+ - **Spec-driven.** Adding a new endpoint to the API surfaces it automatically the next time the cache is refreshed.
666
+ - **No bundled spec.** The first `cycode platform` invocation after install (or after the 24h cache expires) performs a network fetch. On slow connections this first call may take a few seconds; subsequent calls are near-instant until the cache expires.
667
+ - **Override the cache TTL** with `CYCODE_SPEC_CACHE_TTL=<seconds>`.
668
+
669
+
608
670
  # Scan Command
609
671
 
610
672
  ## Running a Scan
@@ -0,0 +1 @@
1
+ __version__ = '3.13.1.dev1' # DON'T TOUCH. Placeholder. Will be filled automatically on poetry build from Git Tag
@@ -2,6 +2,7 @@ import logging
2
2
  import sys
3
3
  from typing import Annotated, Optional
4
4
 
5
+ import click
5
6
  import typer
6
7
  from typer import rich_utils
7
8
  from typer._completion_classes import completion_init
@@ -10,6 +11,7 @@ from typer.completion import install_callback, show_callback
10
11
 
11
12
  from cycode import __version__
12
13
  from cycode.cli.apps import ai_guardrails, ai_remediation, auth, configure, ignore, report, report_import, scan, status
14
+ from cycode.cli.apps.api import get_platform_group
13
15
 
14
16
  if sys.version_info >= (3, 10):
15
17
  from cycode.cli.apps import mcp
@@ -56,6 +58,27 @@ app.add_typer(status.app)
56
58
  if sys.version_info >= (3, 10):
57
59
  app.add_typer(mcp.app)
58
60
 
61
+ # Register the `platform` command group (dynamically built from the OpenAPI spec).
62
+ # The group itself is constructed cheaply at import time; the spec is only fetched
63
+ # when the user actually invokes `cycode platform ...`. Unrelated commands like
64
+ # `cycode scan` and `cycode status` never trigger a spec fetch.
65
+ #
66
+ # Typer doesn't support adding native Click groups directly, so we monkey-patch
67
+ # typer.main.get_group to inject our `platform` group into the resolved Click group.
68
+ # The `app_typer is app` guard ensures we only modify our own app.
69
+ _platform_group = get_platform_group()
70
+ _original_get_group = typer.main.get_group
71
+
72
+
73
+ def _get_group_with_platform(app_typer: typer.Typer) -> click.Group:
74
+ group = _original_get_group(app_typer)
75
+ if app_typer is app and _platform_group.name not in group.commands:
76
+ group.add_command(_platform_group, _platform_group.name)
77
+ return group
78
+
79
+
80
+ typer.main.get_group = _get_group_with_platform
81
+
59
82
 
60
83
  def check_latest_version_on_close(ctx: typer.Context) -> None:
61
84
  output = ctx.obj.get('output')
@@ -0,0 +1,69 @@
1
+ """Cycode platform API CLI commands.
2
+
3
+ Dynamically builds CLI command groups from the Cycode API v4 OpenAPI spec.
4
+ The spec is fetched lazily — only when the user invokes `cycode platform ...` —
5
+ and cached locally for 24 hours.
6
+ """
7
+
8
+ from typing import Any, Optional
9
+
10
+ import click
11
+
12
+ from cycode.logger import get_logger
13
+
14
+ logger = get_logger('Platform')
15
+
16
+ _PLATFORM_HELP = (
17
+ '[BETA] Access the Cycode platform.\n\n'
18
+ 'Commands are generated dynamically from the Cycode API spec and may change '
19
+ 'between releases. The spec is fetched on first use and cached for 24 hours.'
20
+ )
21
+
22
+
23
+ class PlatformGroup(click.Group):
24
+ """Lazy-loading Click group for `cycode platform` subcommands.
25
+
26
+ The OpenAPI spec is only fetched when the user actually invokes
27
+ `cycode platform ...` (or asks for its help). Unrelated commands like
28
+ `cycode scan` or `cycode status` never trigger a spec fetch.
29
+ """
30
+
31
+ def __init__(self, *args: Any, **kwargs: Any) -> None:
32
+ super().__init__(*args, **kwargs)
33
+ self._loaded: bool = False
34
+
35
+ def _ensure_loaded(self, ctx: Optional[click.Context]) -> None:
36
+ if self._loaded:
37
+ return
38
+ self._loaded = True # set first to avoid re-entrancy on errors
39
+
40
+ client_id = client_secret = None
41
+ if ctx is not None:
42
+ root = ctx.find_root()
43
+ if root.obj:
44
+ client_id = root.obj.get('client_id')
45
+ client_secret = root.obj.get('client_secret')
46
+
47
+ try:
48
+ from cycode.cli.apps.api.api_command import build_api_command_groups
49
+
50
+ for sub_group, name in build_api_command_groups(client_id, client_secret):
51
+ if name not in self.commands:
52
+ self.add_command(sub_group, name)
53
+ except Exception as e:
54
+ logger.debug('Could not load platform commands: %s', e)
55
+ # Surface the error to the user only when they're inside `platform`
56
+ click.echo(f'Error loading Cycode platform commands: {e}', err=True)
57
+
58
+ def list_commands(self, ctx: click.Context) -> list[str]:
59
+ self._ensure_loaded(ctx)
60
+ return super().list_commands(ctx)
61
+
62
+ def get_command(self, ctx: click.Context, cmd_name: str) -> Optional[click.Command]:
63
+ self._ensure_loaded(ctx)
64
+ return super().get_command(ctx, cmd_name)
65
+
66
+
67
+ def get_platform_group() -> click.Group:
68
+ """Return the top-level `platform` Click group (lazy-loading)."""
69
+ return PlatformGroup(name='platform', help=_PLATFORM_HELP, no_args_is_help=True)
@@ -0,0 +1,271 @@
1
+ """OpenAPI-to-Typer translator: dynamically builds CLI commands from the Cycode API v4 spec."""
2
+
3
+ import json
4
+ import re
5
+ from typing import Any, Optional
6
+
7
+ import click
8
+
9
+ from cycode.cli.apps.api.openapi_spec import OpenAPISpecError, get_openapi_spec, parse_spec_commands
10
+ from cycode.logger import get_logger
11
+
12
+ logger = get_logger('API Command')
13
+
14
+ # Map OpenAPI parameter types to Click types
15
+ _CLICK_TYPE_MAP: dict[str, click.ParamType] = {
16
+ 'string': click.STRING,
17
+ 'integer': click.INT,
18
+ 'number': click.FLOAT,
19
+ 'boolean': click.BOOL,
20
+ }
21
+
22
+
23
+ def _normalize_tag(tag: str) -> str:
24
+ """Normalize an OpenAPI tag to a CLI-friendly command name.
25
+
26
+ 'Scan Statistics' -> 'scan-statistics'
27
+ 'CLI scan statistics' -> 'cli-scan-statistics'
28
+ """
29
+ return re.sub(r'[^a-z0-9]+', '-', tag.lower()).strip('-')
30
+
31
+
32
+ def _find_common_prefix(paths: list[str]) -> str:
33
+ """Find the longest common path prefix shared by all paths."""
34
+ if not paths:
35
+ return ''
36
+ if len(paths) == 1:
37
+ # For single-path tags, use the parent directory as prefix
38
+ return '/'.join(paths[0].split('/')[:-1])
39
+
40
+ common = paths[0]
41
+ for p in paths[1:]:
42
+ while not p.startswith(common + '/') and common != p:
43
+ common = '/'.join(common.split('/')[:-1])
44
+ return common
45
+
46
+
47
+ def _path_to_command_name(path: str, common_prefix: str, has_path_params: bool) -> str:
48
+ """Derive a CLI command name from an API path relative to the tag's common prefix.
49
+
50
+ Rules:
51
+ 1. Strip the common prefix shared by all endpoints in the tag
52
+ 2. Remove path parameter segments ({id})
53
+ 3. If nothing remains: 'list' (no path params) or 'view' (has path params)
54
+ 4. Otherwise: use remaining segments joined with hyphens
55
+
56
+ Examples:
57
+ /v4/projects (prefix=/v4/projects) -> list
58
+ /v4/projects/{id} (prefix=/v4/projects) -> view
59
+ /v4/projects/assets (prefix=/v4/projects) -> assets
60
+ /v4/violations/count (prefix=/v4/violations) -> count
61
+ """
62
+ # Strip common prefix
63
+ relative = path[len(common_prefix) :] if path.startswith(common_prefix) else path
64
+ relative = relative.strip('/')
65
+
66
+ # Remove path parameter segments and empty parts
67
+ parts = [p for p in relative.split('/') if p and not p.startswith('{')]
68
+
69
+ if not parts:
70
+ return 'view' if has_path_params else 'list'
71
+
72
+ # Join remaining segments with hyphens, normalize to kebab-case
73
+ return re.sub(r'[^a-z0-9]+', '-', '-'.join(parts).lower()).strip('-')
74
+
75
+
76
+ def _param_to_option_name(name: str) -> str:
77
+ """Convert an OpenAPI parameter name to a CLI option name.
78
+
79
+ 'page_size' -> '--page-size'
80
+ 'pageSize' -> '--page-size'
81
+ 'filter.status' -> '--filter-status'
82
+ """
83
+ s = re.sub(r'([a-z])([A-Z])', r'\1-\2', name)
84
+ # Replace any non-alphanumeric characters with hyphens
85
+ s = re.sub(r'[^a-z0-9]+', '-', s.lower()).strip('-')
86
+ return f'--{s}'
87
+
88
+
89
+ def _make_api_request(
90
+ endpoint_path: str,
91
+ method: str,
92
+ path_params: dict[str, str],
93
+ query_params: dict[str, Any],
94
+ client_id: Optional[str] = None,
95
+ client_secret: Optional[str] = None,
96
+ ) -> dict:
97
+ """Execute an API request using the CLI's standard auth client."""
98
+ from urllib.parse import quote
99
+
100
+ from cycode.cli.apps.api.openapi_spec import resolve_credentials
101
+ from cycode.cyclient.cycode_token_based_client import CycodeTokenBasedClient
102
+
103
+ cid, csecret = resolve_credentials(client_id, client_secret)
104
+ client = CycodeTokenBasedClient(cid, csecret)
105
+
106
+ # Substitute path parameters (URL-encoded to prevent path traversal)
107
+ url_path = endpoint_path
108
+ for param_name, param_value in path_params.items():
109
+ url_path = url_path.replace(f'{{{param_name}}}', quote(str(param_value), safe=''))
110
+
111
+ filtered_query = {k: v for k, v in query_params.items() if v is not None}
112
+
113
+ response = client.get(url_path.lstrip('/'), params=filtered_query)
114
+ return response.json()
115
+
116
+
117
+ def build_api_command_groups(
118
+ client_id: Optional[str] = None,
119
+ client_secret: Optional[str] = None,
120
+ ) -> list[tuple[click.Group, str]]:
121
+ """Build Click command groups from the OpenAPI spec.
122
+
123
+ Returns a list of (click_group, command_name) tuples.
124
+ """
125
+ try:
126
+ spec = get_openapi_spec(client_id, client_secret)
127
+ except OpenAPISpecError as e:
128
+ logger.warning('Could not load OpenAPI spec: %s', e)
129
+ return []
130
+
131
+ groups = parse_spec_commands(spec)
132
+ result = []
133
+
134
+ for tag, endpoints in groups.items():
135
+ tag_name = _normalize_tag(tag)
136
+
137
+ group = click.Group(name=tag_name, help=f'[BETA] {tag}')
138
+
139
+ # Compute common prefix from all GET (non-deprecated) endpoint paths in this tag
140
+ get_endpoints = [ep for ep in endpoints if ep['method'] == 'get' and not ep.get('deprecated')]
141
+ if not get_endpoints:
142
+ continue
143
+
144
+ clean_paths = [re.sub(r'/\{[^}]+\}', '', ep['path']) for ep in get_endpoints]
145
+ common_prefix = _find_common_prefix(clean_paths)
146
+
147
+ used_names: dict[str, int] = {}
148
+
149
+ for endpoint in get_endpoints:
150
+ has_path_params = bool(endpoint['path_params'])
151
+ cmd_name = _path_to_command_name(endpoint['path'], common_prefix, has_path_params)
152
+
153
+ # Fix redundancy: if command name matches the tag name, use list/view
154
+ # e.g. "cycode groups groups" -> "cycode groups list"
155
+ if cmd_name == tag_name:
156
+ cmd_name = 'view' if has_path_params else 'list'
157
+
158
+ # Handle duplicate names (e.g. deprecated + new endpoint for same resource)
159
+ if cmd_name in used_names:
160
+ used_names[cmd_name] += 1
161
+ cmd_name = f'{cmd_name}-v{used_names[cmd_name]}'
162
+ else:
163
+ used_names[cmd_name] = 1
164
+
165
+ cmd = _build_endpoint_command(cmd_name, endpoint)
166
+ group.add_command(cmd, cmd_name)
167
+
168
+ result.append((group, tag_name))
169
+
170
+ return result
171
+
172
+
173
+ def _build_click_params(endpoint: dict) -> list[click.Parameter]:
174
+ """Build Click parameters from OpenAPI endpoint definition."""
175
+ params: list[click.Parameter] = []
176
+
177
+ # Path parameters -> required arguments
178
+ for p in endpoint['path_params']:
179
+ param_type = _CLICK_TYPE_MAP.get(p.get('schema', {}).get('type', 'string'), click.STRING)
180
+ params.append(
181
+ click.Argument(
182
+ [p['name'].replace('-', '_')],
183
+ type=param_type,
184
+ required=True,
185
+ )
186
+ )
187
+
188
+ # Query parameters -> --option flags
189
+ for p in endpoint['query_params']:
190
+ param_type = _CLICK_TYPE_MAP.get(p.get('schema', {}).get('type', 'string'), click.STRING)
191
+ option_name = _param_to_option_name(p['name'])
192
+ required = p.get('required', False)
193
+ default = p.get('schema', {}).get('default')
194
+
195
+ schema = p.get('schema', {})
196
+ if 'enum' in schema:
197
+ param_type = click.Choice(schema['enum'])
198
+
199
+ params.append(
200
+ click.Option(
201
+ [option_name],
202
+ type=param_type,
203
+ required=required,
204
+ default=default,
205
+ help=p.get('description', ''),
206
+ show_default=default is not None,
207
+ )
208
+ )
209
+
210
+ return params
211
+
212
+
213
+ def _build_endpoint_command(cmd_name: str, endpoint: dict) -> click.Command:
214
+ """Build a Click command for an API endpoint.
215
+
216
+ Path parameters become required CLI arguments.
217
+ Query parameters become --option flags with proper types.
218
+ """
219
+ ep_path = endpoint['path']
220
+ ep_method = endpoint['method']
221
+ ep_path_params = list(endpoint['path_params'])
222
+ ep_query_params = list(endpoint['query_params'])
223
+ ep_description = endpoint['description'] or endpoint['summary']
224
+
225
+ # Build a mapping from Click's normalized kwarg name to original OpenAPI param name
226
+ _path_param_map = {p['name'].replace('-', '_').lower(): p['name'] for p in ep_path_params}
227
+ _query_param_map = {re.sub(r'[^a-z0-9]+', '_', p['name'].lower()).strip('_'): p['name'] for p in ep_query_params}
228
+
229
+ def _callback(**kwargs: Any) -> None:
230
+ ctx = click.get_current_context()
231
+
232
+ # Extract path param values using the mapping
233
+ path_values = {}
234
+ for kwarg_key, original_name in _path_param_map.items():
235
+ if kwarg_key in kwargs and kwargs[kwarg_key] is not None:
236
+ path_values[original_name] = kwargs[kwarg_key]
237
+
238
+ # Extract query param values (skip None)
239
+ query_values = {}
240
+ for kwarg_key, original_name in _query_param_map.items():
241
+ value = kwargs.get(kwarg_key)
242
+ if value is not None:
243
+ query_values[original_name] = value
244
+
245
+ # Get auth from root context (set by app_callback)
246
+ root_ctx = ctx.find_root()
247
+ client_id = root_ctx.obj.get('client_id') if root_ctx.obj else None
248
+ client_secret = root_ctx.obj.get('client_secret') if root_ctx.obj else None
249
+
250
+ try:
251
+ result = _make_api_request(
252
+ ep_path,
253
+ ep_method,
254
+ path_values,
255
+ query_values,
256
+ client_id=client_id,
257
+ client_secret=client_secret,
258
+ )
259
+ except Exception as e:
260
+ click.echo(f'Error: {e}', err=True)
261
+ raise click.Abort from e
262
+
263
+ click.echo(json.dumps(result, indent=2))
264
+
265
+ return click.Command(
266
+ name=cmd_name,
267
+ callback=_callback,
268
+ help=ep_description,
269
+ short_help=endpoint['summary'],
270
+ params=_build_click_params(endpoint),
271
+ )