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.
- {cycode-3.13.0 → cycode-3.13.1.dev1}/PKG-INFO +64 -2
- {cycode-3.13.0 → cycode-3.13.1.dev1}/README.md +63 -1
- cycode-3.13.1.dev1/cycode/__init__.py +1 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/app.py +23 -0
- cycode-3.13.1.dev1/cycode/cli/apps/api/__init__.py +69 -0
- cycode-3.13.1.dev1/cycode/cli/apps/api/api_command.py +271 -0
- cycode-3.13.1.dev1/cycode/cli/apps/api/openapi_spec.py +182 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/pyproject.toml +1 -1
- cycode-3.13.0/cycode/__init__.py +0 -1
- {cycode-3.13.0 → cycode-3.13.1.dev1}/LICENCE +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/__main__.py +0 -0
- {cycode-3.13.0/cycode/cyclient → cycode-3.13.1.dev1/cycode/cli}/__init__.py +0 -0
- {cycode-3.13.0/cycode/cli/utils → cycode-3.13.1.dev1/cycode/cli/apps}/__init__.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/activation_manager.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/ai_guardrails/__init__.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/ai_guardrails/command_utils.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/ai_guardrails/consts.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/ai_guardrails/ensure_auth_command.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/ai_guardrails/hooks_manager.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/ai_guardrails/install_command.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/ai_guardrails/scan/__init__.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/ai_guardrails/scan/claude_config.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/ai_guardrails/scan/consts.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/ai_guardrails/scan/handlers.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/ai_guardrails/scan/payload.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/ai_guardrails/scan/policy.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/ai_guardrails/scan/response_builders.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/ai_guardrails/scan/scan_command.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/ai_guardrails/scan/types.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/ai_guardrails/scan/utils.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/ai_guardrails/status_command.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/ai_guardrails/uninstall_command.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/ai_remediation/__init__.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/ai_remediation/ai_remediation_command.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/ai_remediation/apply_fix.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/ai_remediation/print_remediation.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/auth/__init__.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/auth/auth_command.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/auth/auth_common.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/auth/auth_manager.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/auth/models.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/configure/__init__.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/configure/configure_command.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/configure/consts.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/configure/messages.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/configure/prompts.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/ignore/__init__.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/ignore/ignore_command.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/mcp/__init__.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/mcp/mcp_command.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/report/__init__.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/report/report_command.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/report/sbom/__init__.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/report/sbom/common.py +0 -0
- {cycode-3.13.0/cycode/cli/user_settings → cycode-3.13.1.dev1/cycode/cli/apps/report/sbom/path}/__init__.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/report/sbom/path/path_command.py +0 -0
- {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
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/report/sbom/repository_url/repository_url_command.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/report/sbom/sbom_command.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/report/sbom/sbom_report_file.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/report_import/__init__.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/report_import/report_import_command.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/report_import/sbom/__init__.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/report_import/sbom/sbom_command.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/sca_options.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/scan/__init__.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/scan/aggregation_report.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/scan/code_scanner.py +0 -0
- {cycode-3.13.0/cycode/cli/printers/tables → cycode-3.13.1.dev1/cycode/cli/apps/scan/commit_history}/__init__.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/scan/commit_history/commit_history_command.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/scan/commit_range_scanner.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/scan/detection_excluder.py +0 -0
- {cycode-3.13.0/cycode/cli/files_collector/sca/sbt → cycode-3.13.1.dev1/cycode/cli/apps/scan/path}/__init__.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/scan/path/path_command.py +0 -0
- {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
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/scan/pre_commit/pre_commit_command.py +0 -0
- {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
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/scan/pre_push/pre_push_command.py +0 -0
- {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
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/scan/pre_receive/pre_receive_command.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/scan/remote_url_resolver.py +0 -0
- {cycode-3.13.0/cycode/cli/files_collector/sca/nuget → cycode-3.13.1.dev1/cycode/cli/apps/scan/repository}/__init__.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/scan/repository/repository_command.py +0 -0
- {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
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/scan/scan_ci/ci_integrations.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/scan/scan_ci/scan_ci_command.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/scan/scan_command.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/scan/scan_parameters.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/scan/scan_result.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/status/__init__.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/status/get_cli_status.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/status/models.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/status/status_command.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/apps/status/version_command.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/cli_types.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/config.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/console.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/consts.py +0 -0
- {cycode-3.13.0/cycode/cli/files_collector/sca/maven → cycode-3.13.1.dev1/cycode/cli/exceptions}/__init__.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/exceptions/custom_exceptions.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/exceptions/handle_ai_remediation_errors.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/exceptions/handle_auth_errors.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/exceptions/handle_errors.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/exceptions/handle_report_sbom_errors.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/exceptions/handle_scan_errors.py +0 -0
- {cycode-3.13.0/cycode/cli/files_collector/sca/go → cycode-3.13.1.dev1/cycode/cli/files_collector}/__init__.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/files_collector/commit_range_documents.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/files_collector/documents_walk_ignore.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/files_collector/file_excluder.py +0 -0
- {cycode-3.13.0/cycode/cli/files_collector/sca → cycode-3.13.1.dev1/cycode/cli/files_collector/iac}/__init__.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/files_collector/iac/tf_content_generator.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/files_collector/models/__init__.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/files_collector/models/in_memory_zip.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/files_collector/path_documents.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/files_collector/repository_documents.py +0 -0
- {cycode-3.13.0/cycode/cli/files_collector/iac → cycode-3.13.1.dev1/cycode/cli/files_collector/sca}/__init__.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/files_collector/sca/base_restore_dependencies.py +0 -0
- {cycode-3.13.0/cycode/cli/files_collector → cycode-3.13.1.dev1/cycode/cli/files_collector/sca/go}/__init__.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/files_collector/sca/go/restore_go_dependencies.py +0 -0
- {cycode-3.13.0/cycode/cli/exceptions → cycode-3.13.1.dev1/cycode/cli/files_collector/sca/maven}/__init__.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/files_collector/sca/maven/restore_gradle_dependencies.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/files_collector/sca/maven/restore_maven_dependencies.py +0 -0
- {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
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/files_collector/sca/npm/restore_deno_dependencies.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/files_collector/sca/npm/restore_npm_dependencies.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/files_collector/sca/npm/restore_pnpm_dependencies.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/files_collector/sca/npm/restore_yarn_dependencies.py +0 -0
- {cycode-3.13.0/cycode/cli/apps/scan/repository → cycode-3.13.1.dev1/cycode/cli/files_collector/sca/nuget}/__init__.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/files_collector/sca/nuget/restore_nuget_dependencies.py +0 -0
- {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
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/files_collector/sca/php/restore_composer_dependencies.py +0 -0
- {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
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/files_collector/sca/python/restore_pipenv_dependencies.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/files_collector/sca/python/restore_poetry_dependencies.py +0 -0
- {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
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/files_collector/sca/ruby/restore_ruby_dependencies.py +0 -0
- {cycode-3.13.0/cycode/cli/apps/scan/path → cycode-3.13.1.dev1/cycode/cli/files_collector/sca/sbt}/__init__.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/files_collector/sca/sbt/restore_sbt_dependencies.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/files_collector/sca/sca_file_collector.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/files_collector/walk_ignore.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/files_collector/zip_documents.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/logger.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/main.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/models.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/printers/__init__.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/printers/console_printer.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/printers/json_printer.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/printers/printer_base.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/printers/rich_printer.py +0 -0
- {cycode-3.13.0/cycode/cli/apps/scan/commit_history → cycode-3.13.1.dev1/cycode/cli/printers/tables}/__init__.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/printers/tables/sca_table_printer.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/printers/tables/table.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/printers/tables/table_models.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/printers/tables/table_printer.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/printers/tables/table_printer_base.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/printers/text_printer.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/printers/utils/__init__.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/printers/utils/code_snippet_syntax.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/printers/utils/detection_data.py +0 -0
- {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
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/printers/utils/detection_ordering/common_ordering.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/printers/utils/detection_ordering/sca_ordering.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/printers/utils/rich_helpers.py +0 -0
- {cycode-3.13.0/cycode/cli/apps/report/sbom/path → cycode-3.13.1.dev1/cycode/cli/user_settings}/__init__.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/user_settings/base_file_manager.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/user_settings/config_file_manager.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/user_settings/configuration_manager.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/user_settings/credentials_manager.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/user_settings/jwt_creator.py +0 -0
- {cycode-3.13.0/cycode/cli/apps → cycode-3.13.1.dev1/cycode/cli/utils}/__init__.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/utils/binary_utils.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/utils/enum_utils.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/utils/get_api_client.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/utils/git_proxy.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/utils/ignore_utils.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/utils/jwt_utils.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/utils/path_utils.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/utils/progress_bar.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/utils/scan_batch.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/utils/scan_utils.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/utils/shell_executor.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/utils/string_utils.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/utils/task_timer.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/utils/url_utils.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/utils/version_checker.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cli/utils/yaml_utils.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/config.py +0 -0
- {cycode-3.13.0/cycode/cli → cycode-3.13.1.dev1/cycode/cyclient}/__init__.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cyclient/ai_security_manager_client.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cyclient/ai_security_manager_service_config.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cyclient/auth_client.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cyclient/base_token_auth_client.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cyclient/cli_activation_client.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cyclient/client_creator.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cyclient/config.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cyclient/config_dev.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cyclient/cycode_client.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cyclient/cycode_client_base.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cyclient/cycode_dev_based_client.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cyclient/cycode_oidc_based_client.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cyclient/cycode_token_based_client.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cyclient/headers.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cyclient/import_sbom_client.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cyclient/logger.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cyclient/models.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cyclient/report_client.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cyclient/scan_client.py +0 -0
- {cycode-3.13.0 → cycode-3.13.1.dev1}/cycode/cyclient/scan_config_base.py +0 -0
- {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.
|
|
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. [
|
|
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. [
|
|
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
|
+
)
|