apm-cli 0.17.0__tar.gz → 0.18.0__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.
- {apm_cli-0.17.0/src/apm_cli.egg-info → apm_cli-0.18.0}/PKG-INFO +1 -1
- {apm_cli-0.17.0 → apm_cli-0.18.0}/pyproject.toml +1 -1
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/adapters/client/base.py +20 -7
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/adapters/client/copilot.py +12 -13
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/adapters/client/intellij.py +11 -6
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/commands/install.py +28 -7
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/commands/marketplace/__init__.py +37 -7
- apm_cli-0.18.0/src/apm_cli/commands/marketplace/audit.py +122 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/commands/outdated.py +80 -8
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/compilation/context_optimizer.py +28 -9
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/compilation/link_resolver.py +18 -1
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/core/command_logger.py +5 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/deps/apm_resolver.py +111 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/deps/dependency_graph.py +1 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/deps/git_semver_resolver.py +9 -5
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/deps/lockfile.py +13 -1
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/deps/plugin_parser.py +156 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/install/context.py +1 -0
- apm_cli-0.18.0/src/apm_cli/install/lsp/__init__.py +5 -0
- apm_cli-0.18.0/src/apm_cli/install/lsp/integration.py +148 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/install/phases/resolve.py +33 -2
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/install/pipeline.py +3 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/install/summary.py +4 -1
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/integration/__init__.py +2 -0
- apm_cli-0.18.0/src/apm_cli/integration/_shared.py +71 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/integration/base_integrator.py +8 -1
- apm_cli-0.18.0/src/apm_cli/integration/lsp_integrator.py +508 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/integration/mcp_integrator.py +4 -41
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/integration/mcp_integrator_install.py +1 -28
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/integration/skill_integrator.py +41 -3
- apm_cli-0.18.0/src/apm_cli/marketplace/audit.py +337 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/marketplace/client.py +96 -14
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/marketplace/resolver.py +59 -10
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/marketplace/tag_pattern.py +80 -3
- apm_cli-0.18.0/src/apm_cli/marketplace/version_resolver.py +114 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/models/apm_package.py +33 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/models/dependency/__init__.py +2 -0
- apm_cli-0.18.0/src/apm_cli/models/dependency/lsp.py +229 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/models/dependency/reference.py +129 -3
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/utils/exclude.py +10 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0/src/apm_cli.egg-info}/PKG-INFO +1 -1
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli.egg-info/SOURCES.txt +8 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/tests/test_apm_package_models.py +156 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/tests/test_apm_resolver.py +192 -1
- {apm_cli-0.17.0 → apm_cli-0.18.0}/tests/test_lockfile.py +55 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/AUTHORS +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/LICENSE +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/NOTICE +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/README.md +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/setup.cfg +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/__init__.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/adapters/__init__.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/adapters/client/__init__.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/adapters/client/_mcp_runtime_args.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/adapters/client/claude.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/adapters/client/codex.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/adapters/client/cursor.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/adapters/client/gemini.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/adapters/client/opencode.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/adapters/client/vscode.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/adapters/client/windsurf.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/adapters/package_manager/__init__.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/adapters/package_manager/base.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/adapters/package_manager/default_manager.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/bundle/__init__.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/bundle/local_bundle.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/bundle/lockfile_enrichment.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/bundle/packer.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/bundle/plugin_exporter.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/bundle/unpacker.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/cache/__init__.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/cache/git_cache.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/cache/http_cache.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/cache/integrity.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/cache/locking.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/cache/paths.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/cache/url_normalize.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/cli.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/commands/__init__.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/commands/_apm_yml_writer.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/commands/_helpers.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/commands/audit.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/commands/cache.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/commands/compile/__init__.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/commands/compile/cli.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/commands/compile/watcher.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/commands/config.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/commands/deps/__init__.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/commands/deps/_utils.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/commands/deps/cli.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/commands/deps/why.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/commands/doctor.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/commands/experimental.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/commands/find.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/commands/init.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/commands/list_cmd.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/commands/lock.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/commands/marketplace/check.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/commands/marketplace/doctor.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/commands/marketplace/init.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/commands/marketplace/migrate.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/commands/marketplace/outdated.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/commands/marketplace/plugin/__init__.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/commands/marketplace/plugin/add.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/commands/marketplace/plugin/remove.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/commands/marketplace/plugin/set.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/commands/marketplace/publish.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/commands/marketplace/validate.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/commands/mcp.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/commands/pack.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/commands/plugin/__init__.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/commands/plugin/init.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/commands/policy.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/commands/prune.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/commands/publish.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/commands/run.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/commands/runtime.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/commands/self_update.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/commands/targets.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/commands/uninstall/__init__.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/commands/uninstall/cli.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/commands/uninstall/engine.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/commands/update.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/commands/view.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/compilation/__init__.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/compilation/agents_compiler.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/compilation/build_id.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/compilation/claude_formatter.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/compilation/constants.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/compilation/constitution.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/compilation/constitution_block.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/compilation/distributed_compiler.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/compilation/gemini_formatter.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/compilation/injector.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/compilation/managed_section.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/compilation/output_writer.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/compilation/template_builder.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/config.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/constants.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/core/__init__.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/core/apm_yml.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/core/auth.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/core/azure_cli.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/core/build_orchestrator.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/core/conflict_detector.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/core/docker_args.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/core/errors.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/core/experimental.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/core/install_audit.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/core/null_logger.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/core/operations.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/core/plugin_manifest.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/core/safe_installer.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/core/scope.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/core/script_runner.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/core/target_detection.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/core/token_manager.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/deps/__init__.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/deps/_shared.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/deps/aggregator.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/deps/artifactory_entry.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/deps/artifactory_orchestrator.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/deps/bare_cache.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/deps/clone_engine.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/deps/download_strategies.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/deps/git_auth_env.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/deps/git_reference_resolver.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/deps/git_remote_ops.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/deps/github_downloader.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/deps/github_downloader_validation.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/deps/host_backends.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/deps/installed_package.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/deps/outdated_row.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/deps/package_validator.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/deps/path_anchoring.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/deps/registry/__init__.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/deps/registry/auth.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/deps/registry/client.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/deps/registry/config_loader.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/deps/registry/extractor.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/deps/registry/feature_gate.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/deps/registry/outdated.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/deps/registry/resolver.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/deps/registry/semver.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/deps/registry_proxy.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/deps/shared_clone_cache.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/deps/tiered_ref_resolver.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/deps/transport_selection.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/deps/verifier.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/deps/why_walker.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/drift.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/factory.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/install/__init__.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/install/artifactory_resolver.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/install/cache_pin.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/install/drift.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/install/errors.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/install/gitlab_resolver.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/install/heals/__init__.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/install/heals/base.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/install/heals/branch_ref_drift.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/install/heals/buggy_lockfile_recovery.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/install/helpers/__init__.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/install/helpers/security_scan.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/install/insecure_policy.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/install/local_bundle_handler.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/install/mcp/__init__.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/install/mcp/args.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/install/mcp/command.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/install/mcp/conflicts.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/install/mcp/entry.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/install/mcp/registry.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/install/mcp/warnings.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/install/mcp/writer.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/install/package_resolution.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/install/package_selection.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/install/phases/__init__.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/install/phases/_redownload.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/install/phases/_skip_logic.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/install/phases/audit.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/install/phases/cleanup.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/install/phases/download.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/install/phases/finalize.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/install/phases/heal.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/install/phases/integrate.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/install/phases/local_content.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/install/phases/lockfile.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/install/phases/policy_gate.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/install/phases/policy_target_check.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/install/phases/post_deps_local.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/install/phases/targets.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/install/plan.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/install/presentation/__init__.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/install/presentation/dry_run.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/install/registry_wiring.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/install/request.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/install/root_redirect.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/install/service.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/install/services.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/install/skill_path_migration.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/install/sources.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/install/template.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/install/validation.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/integration/agent_integrator.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/integration/cleanup.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/integration/command_integrator.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/integration/copilot_app_db.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/integration/copilot_app_project.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/integration/copilot_app_workflow_integrator.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/integration/copilot_app_ws.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/integration/copilot_cowork_paths.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/integration/coverage.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/integration/dispatch.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/integration/hook_integrator.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/integration/instruction_integrator.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/integration/opencode_frontmatter.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/integration/prompt_integrator.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/integration/skill_transformer.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/integration/targets.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/integration/utils.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/marketplace/__init__.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/marketplace/_git_utils.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/marketplace/_io.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/marketplace/_shared.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/marketplace/builder.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/marketplace/diagnostics.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/marketplace/drift_check.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/marketplace/errors.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/marketplace/git_stderr.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/marketplace/init_template.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/marketplace/migration.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/marketplace/models.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/marketplace/output_mappers.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/marketplace/output_profiles.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/marketplace/pr_integration.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/marketplace/publisher.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/marketplace/ref_resolver.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/marketplace/registry.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/marketplace/semver.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/marketplace/shadow_detector.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/marketplace/validator.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/marketplace/version_check.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/marketplace/version_pins.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/marketplace/yml_editor.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/marketplace/yml_schema.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/models/__init__.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/models/dependency/mcp.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/models/dependency/types.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/models/format_detection.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/models/plugin.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/models/results.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/models/validation.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/output/__init__.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/output/formatters.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/output/models.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/output/script_formatters.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/policy/__init__.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/policy/_constraint_pinning.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/policy/_help_text.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/policy/_shared.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/policy/ci_checks.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/policy/discovery.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/policy/inheritance.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/policy/install_preflight.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/policy/matcher.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/policy/models.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/policy/outcome_routing.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/policy/parser.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/policy/policy_checks.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/policy/project_config.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/policy/schema.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/primitives/__init__.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/primitives/discovery.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/primitives/models.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/primitives/parser.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/registry/__init__.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/registry/client.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/registry/integration.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/registry/operations.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/runtime/__init__.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/runtime/base.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/runtime/codex_runtime.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/runtime/copilot_runtime.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/runtime/factory.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/runtime/llm_runtime.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/runtime/manager.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/runtime/utils.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/security/__init__.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/security/audit_report.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/security/content_scanner.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/security/external/__init__.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/security/external/base.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/security/external/gate.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/security/external/generic_sarif.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/security/external/options.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/security/external/registry.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/security/external/runner.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/security/external/sarif_ingest.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/security/external/skillspector.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/security/file_scanner.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/security/gate.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/update_policy.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/utils/__init__.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/utils/atomic_io.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/utils/console.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/utils/content_hash.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/utils/diagnostics.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/utils/file_ops.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/utils/git_env.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/utils/git_sparse.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/utils/github_host.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/utils/guards.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/utils/helpers.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/utils/install_tui.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/utils/normalization.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/utils/path_security.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/utils/paths.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/utils/patterns.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/utils/perf_stats.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/utils/reflink.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/utils/short_sha.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/utils/subprocess_env.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/utils/version_checker.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/utils/yaml_io.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/version.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/workflow/__init__.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/workflow/discovery.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/workflow/parser.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli/workflow/runner.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli.egg-info/dependency_links.txt +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli.egg-info/entry_points.txt +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli.egg-info/requires.txt +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/src/apm_cli.egg-info/top_level.txt +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/tests/test_codex_docker_args_fix.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/tests/test_codex_empty_string_and_defaults.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/tests/test_collision_integration.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/tests/test_console.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/tests/test_distributed_compilation.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/tests/test_empty_string_and_defaults.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/tests/test_enhanced_discovery.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/tests/test_github_downloader.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/tests/test_github_downloader_token_precedence.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/tests/test_runnable_prompts.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/tests/test_runtime_manager_token_precedence.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/tests/test_token_manager.py +0 -0
- {apm_cli-0.17.0 → apm_cli-0.18.0}/tests/test_virtual_package_multi_install.py +0 -0
|
@@ -150,6 +150,21 @@ class MCPClientAdapter(ABC):
|
|
|
150
150
|
self._last_env_placeholder_keys: set[str] = set()
|
|
151
151
|
self._last_legacy_angle_vars: set[str] = set()
|
|
152
152
|
|
|
153
|
+
def _format_runtime_env_placeholder(self, name: str) -> str:
|
|
154
|
+
"""Return the target runtime's env-var placeholder syntax for *name*."""
|
|
155
|
+
return "${" + name + "}"
|
|
156
|
+
|
|
157
|
+
def _translate_env_placeholder_for_runtime(self, value):
|
|
158
|
+
"""Translate env-var placeholders to this adapter's runtime syntax."""
|
|
159
|
+
if not isinstance(value, str):
|
|
160
|
+
return value
|
|
161
|
+
|
|
162
|
+
def _to_runtime(match):
|
|
163
|
+
var_name = match.group(1) or match.group(2)
|
|
164
|
+
return self._format_runtime_env_placeholder(var_name)
|
|
165
|
+
|
|
166
|
+
return _ENV_PLACEHOLDER_RE.sub(_to_runtime, value)
|
|
167
|
+
|
|
153
168
|
@property
|
|
154
169
|
def project_root(self) -> Path:
|
|
155
170
|
"""Return the explicit project root or the current working directory."""
|
|
@@ -390,9 +405,7 @@ class MCPClientAdapter(ABC):
|
|
|
390
405
|
continue
|
|
391
406
|
if _has_env_placeholder(raw_value):
|
|
392
407
|
self._last_legacy_angle_vars.update(_extract_legacy_angle_vars(raw_value))
|
|
393
|
-
translated[name] =
|
|
394
|
-
# Record every ${VAR} in the translated value (handles
|
|
395
|
-
# both ${env:VAR} -> ${VAR} and bare ${VAR} cases).
|
|
408
|
+
translated[name] = self._translate_env_placeholder_for_runtime(raw_value)
|
|
396
409
|
placeholder_keys.extend(
|
|
397
410
|
m.group(1) for m in _ENV_VAR_RE.finditer(translated[name])
|
|
398
411
|
)
|
|
@@ -403,7 +416,7 @@ class MCPClientAdapter(ABC):
|
|
|
403
416
|
else:
|
|
404
417
|
# Literal value present in apm.yml -- replace with a
|
|
405
418
|
# runtime placeholder so the secret never touches disk.
|
|
406
|
-
translated[name] =
|
|
419
|
+
translated[name] = self._format_runtime_env_placeholder(name)
|
|
407
420
|
placeholder_keys.append(name)
|
|
408
421
|
self._last_env_placeholder_keys = set(placeholder_keys)
|
|
409
422
|
return translated
|
|
@@ -421,7 +434,7 @@ class MCPClientAdapter(ABC):
|
|
|
421
434
|
if name in self._DEFAULT_GITHUB_ENV:
|
|
422
435
|
resolved[name] = self._DEFAULT_GITHUB_ENV[name]
|
|
423
436
|
else:
|
|
424
|
-
resolved[name] =
|
|
437
|
+
resolved[name] = self._format_runtime_env_placeholder(name)
|
|
425
438
|
placeholder_keys.append(name)
|
|
426
439
|
self._last_env_placeholder_keys = set(placeholder_keys)
|
|
427
440
|
return resolved
|
|
@@ -510,7 +523,7 @@ class MCPClientAdapter(ABC):
|
|
|
510
523
|
self._last_env_placeholder_keys.update(legacy_keys)
|
|
511
524
|
for match in _ENV_VAR_RE.finditer(value):
|
|
512
525
|
self._last_env_placeholder_keys.add(match.group(1))
|
|
513
|
-
return
|
|
526
|
+
return self._translate_env_placeholder_for_runtime(value)
|
|
514
527
|
|
|
515
528
|
from rich.prompt import Prompt
|
|
516
529
|
|
|
@@ -560,7 +573,7 @@ class MCPClientAdapter(ABC):
|
|
|
560
573
|
|
|
561
574
|
if self._supports_runtime_env_substitution:
|
|
562
575
|
self._last_legacy_angle_vars.update(_extract_legacy_angle_vars(processed))
|
|
563
|
-
processed =
|
|
576
|
+
processed = self._translate_env_placeholder_for_runtime(processed)
|
|
564
577
|
else:
|
|
565
578
|
# Resolve only the legacy ``<VAR>`` form; newer syntaxes are
|
|
566
579
|
# preserved verbatim for backward compatibility.
|
|
@@ -26,7 +26,9 @@ from .base import (
|
|
|
26
26
|
_extract_legacy_angle_vars,
|
|
27
27
|
_has_env_placeholder,
|
|
28
28
|
_stringify_env_literal,
|
|
29
|
-
|
|
29
|
+
)
|
|
30
|
+
from .base import (
|
|
31
|
+
_translate_env_placeholder as _translate_env_placeholder,
|
|
30
32
|
)
|
|
31
33
|
|
|
32
34
|
|
|
@@ -678,9 +680,9 @@ class CopilotClientAdapter(MCPClientAdapter):
|
|
|
678
680
|
# ({NAME: value-or-placeholder}); registry-sourced deps pass a list
|
|
679
681
|
# of {name, description, required} dicts. Translate-mode handling
|
|
680
682
|
# for the dict shape: each value is either already a placeholder
|
|
681
|
-
# (translate it to the
|
|
682
|
-
# the key as a placeholder reference and emit
|
|
683
|
-
# value never lands on disk). See issue #1152.
|
|
683
|
+
# (translate it to the adapter's runtime form) or a literal
|
|
684
|
+
# (record the key as a placeholder reference and emit a runtime
|
|
685
|
+
# placeholder so the value never lands on disk). See issue #1152.
|
|
684
686
|
if isinstance(env_vars, dict) and self._supports_runtime_env_substitution:
|
|
685
687
|
translated = {}
|
|
686
688
|
placeholder_keys = []
|
|
@@ -694,9 +696,7 @@ class CopilotClientAdapter(MCPClientAdapter):
|
|
|
694
696
|
continue
|
|
695
697
|
if _has_env_placeholder(raw_value):
|
|
696
698
|
self._last_legacy_angle_vars.update(_extract_legacy_angle_vars(raw_value))
|
|
697
|
-
translated[name] =
|
|
698
|
-
# Record every ${VAR} in the translated value (handles
|
|
699
|
-
# both ${env:VAR} -> ${VAR} and bare ${VAR} cases).
|
|
699
|
+
translated[name] = self._translate_env_placeholder_for_runtime(raw_value)
|
|
700
700
|
for match in _ENV_VAR_RE.finditer(translated[name]):
|
|
701
701
|
placeholder_keys.append(match.group(1))
|
|
702
702
|
elif name in default_github_env and raw_value == default_github_env[name]:
|
|
@@ -704,7 +704,7 @@ class CopilotClientAdapter(MCPClientAdapter):
|
|
|
704
704
|
else:
|
|
705
705
|
# Literal value present in apm.yml -- replace with a
|
|
706
706
|
# runtime placeholder so the secret never touches disk.
|
|
707
|
-
translated[name] =
|
|
707
|
+
translated[name] = self._format_runtime_env_placeholder(name)
|
|
708
708
|
placeholder_keys.append(name)
|
|
709
709
|
self._last_env_placeholder_keys = set(placeholder_keys)
|
|
710
710
|
return translated
|
|
@@ -722,10 +722,9 @@ class CopilotClientAdapter(MCPClientAdapter):
|
|
|
722
722
|
# Non-secret literal default -- preserve as-is.
|
|
723
723
|
resolved[name] = default_github_env[name]
|
|
724
724
|
else:
|
|
725
|
-
# Emit a runtime-substitution placeholder;
|
|
726
|
-
#
|
|
727
|
-
|
|
728
|
-
resolved[name] = "${" + name + "}"
|
|
725
|
+
# Emit a runtime-substitution placeholder; APM never reads
|
|
726
|
+
# or stores the value.
|
|
727
|
+
resolved[name] = self._format_runtime_env_placeholder(name)
|
|
729
728
|
placeholder_keys.append(name)
|
|
730
729
|
# Record for the post-install summary line and the
|
|
731
730
|
# security-improvement notice.
|
|
@@ -783,7 +782,7 @@ class CopilotClientAdapter(MCPClientAdapter):
|
|
|
783
782
|
# them (the env-block path tracks via _resolve_environment_variables).
|
|
784
783
|
for match in _ENV_VAR_RE.finditer(value):
|
|
785
784
|
self._last_env_placeholder_keys.add(match.group(1))
|
|
786
|
-
return
|
|
785
|
+
return self._translate_env_placeholder_for_runtime(value)
|
|
787
786
|
|
|
788
787
|
import sys
|
|
789
788
|
|
|
@@ -67,9 +67,10 @@ class IntelliJClientAdapter(CopilotClientAdapter):
|
|
|
67
67
|
|
|
68
68
|
JetBrains Copilot stores server definitions in a user-scope JSON file
|
|
69
69
|
with a ``"servers"`` top-level key (not ``"mcpServers"``). This adapter
|
|
70
|
-
inherits
|
|
71
|
-
|
|
72
|
-
``mcp_servers_key
|
|
70
|
+
inherits registry-resolution and config read/write from
|
|
71
|
+
:class:`CopilotClientAdapter` and overrides the config path,
|
|
72
|
+
``mcp_servers_key``, and runtime env-var placeholder syntax so the parent
|
|
73
|
+
methods operate on the correct schema.
|
|
73
74
|
"""
|
|
74
75
|
|
|
75
76
|
supports_user_scope: bool = True
|
|
@@ -77,9 +78,13 @@ class IntelliJClientAdapter(CopilotClientAdapter):
|
|
|
77
78
|
target_name: str = "intellij"
|
|
78
79
|
mcp_servers_key: str = "servers"
|
|
79
80
|
|
|
80
|
-
# JetBrains
|
|
81
|
-
#
|
|
82
|
-
_supports_runtime_env_substitution: bool =
|
|
81
|
+
# JetBrains Copilot resolves ${env:VAR} placeholders in mcp.json at
|
|
82
|
+
# server-start, so APM must not bake matching host secrets to disk.
|
|
83
|
+
_supports_runtime_env_substitution: bool = True
|
|
84
|
+
|
|
85
|
+
def _format_runtime_env_placeholder(self, name: str) -> str:
|
|
86
|
+
"""Return JetBrains Copilot's native env-var placeholder syntax."""
|
|
87
|
+
return "${env:" + name + "}"
|
|
83
88
|
|
|
84
89
|
# ------------------------------------------------------------------ #
|
|
85
90
|
# Config path
|
|
@@ -888,7 +888,7 @@ def _handle_mcp_install(
|
|
|
888
888
|
@click.option(
|
|
889
889
|
"--runtime",
|
|
890
890
|
help=(
|
|
891
|
-
"Target specific runtime only (copilot, codex, vscode, cursor, opencode, gemini, claude, windsurf)"
|
|
891
|
+
"Target specific runtime only (copilot, codex, vscode, cursor, opencode, gemini, claude, windsurf, intellij)"
|
|
892
892
|
),
|
|
893
893
|
)
|
|
894
894
|
@click.option("--exclude", help="Exclude specific runtime from installation")
|
|
@@ -961,7 +961,7 @@ def _handle_mcp_install(
|
|
|
961
961
|
"global_",
|
|
962
962
|
is_flag=True,
|
|
963
963
|
default=False,
|
|
964
|
-
help="Install to user scope (~/.apm/) instead of the current project. MCP servers target global-capable runtimes only (Copilot CLI, Codex CLI).",
|
|
964
|
+
help="Install to user scope (~/.apm/) instead of the current project. MCP servers target global-capable runtimes only (Copilot CLI, Codex CLI, JetBrains Copilot).",
|
|
965
965
|
)
|
|
966
966
|
@click.option(
|
|
967
967
|
"--ssh",
|
|
@@ -1550,7 +1550,7 @@ def install( # noqa: PLR0913
|
|
|
1550
1550
|
skill_subset_from_cli=bool(skill_names),
|
|
1551
1551
|
)
|
|
1552
1552
|
|
|
1553
|
-
apm_count, mcp_count, apm_diagnostics = _install_apm_packages(
|
|
1553
|
+
apm_count, mcp_count, lsp_count, apm_diagnostics = _install_apm_packages(
|
|
1554
1554
|
install_ctx,
|
|
1555
1555
|
outcome,
|
|
1556
1556
|
)
|
|
@@ -1559,6 +1559,7 @@ def install( # noqa: PLR0913
|
|
|
1559
1559
|
logger=logger,
|
|
1560
1560
|
apm_count=apm_count,
|
|
1561
1561
|
mcp_count=mcp_count,
|
|
1562
|
+
lsp_count=lsp_count,
|
|
1562
1563
|
apm_diagnostics=apm_diagnostics,
|
|
1563
1564
|
force=force,
|
|
1564
1565
|
elapsed_seconds=time.perf_counter() - install_started_at,
|
|
@@ -1639,7 +1640,7 @@ def _install_apm_packages(ctx, outcome):
|
|
|
1639
1640
|
``None`` when no explicit packages were passed).
|
|
1640
1641
|
|
|
1641
1642
|
Returns:
|
|
1642
|
-
Tuple of ``(apm_count, mcp_count, apm_diagnostics)``.
|
|
1643
|
+
Tuple of ``(apm_count, mcp_count, lsp_count, apm_diagnostics)``.
|
|
1643
1644
|
"""
|
|
1644
1645
|
logger = ctx.logger
|
|
1645
1646
|
|
|
@@ -1677,6 +1678,7 @@ def _install_apm_packages(ctx, outcome):
|
|
|
1677
1678
|
# Determine what to install based on install mode
|
|
1678
1679
|
should_install_apm = ctx.install_mode != InstallMode.MCP
|
|
1679
1680
|
should_install_mcp = ctx.install_mode != InstallMode.APM
|
|
1681
|
+
should_install_lsp = should_install_mcp
|
|
1680
1682
|
|
|
1681
1683
|
# Show what will be installed if dry run
|
|
1682
1684
|
if ctx.dry_run:
|
|
@@ -1711,7 +1713,7 @@ def _install_apm_packages(ctx, outcome):
|
|
|
1711
1713
|
only_packages=ctx.only_packages,
|
|
1712
1714
|
apm_dir=ctx.apm_dir,
|
|
1713
1715
|
)
|
|
1714
|
-
return 0, 0, None # render_and_exit exits; this line is defensive
|
|
1716
|
+
return 0, 0, 0, None # render_and_exit exits; this line is defensive
|
|
1715
1717
|
|
|
1716
1718
|
# Install APM dependencies first (if requested)
|
|
1717
1719
|
apm_count = 0
|
|
@@ -1945,17 +1947,35 @@ def _install_apm_packages(ctx, outcome):
|
|
|
1945
1947
|
# mcp_servers. Restore the previous set so it is not lost.
|
|
1946
1948
|
MCPIntegrator.update_lockfile(old_mcp_servers, _lock_path, mcp_configs=old_mcp_configs)
|
|
1947
1949
|
|
|
1950
|
+
# -------------------------------------------------------------------------
|
|
1951
|
+
# LSP integration (extracted to install/lsp/integration.py)
|
|
1952
|
+
# -------------------------------------------------------------------------
|
|
1953
|
+
from apm_cli.install.lsp import run_lsp_integration
|
|
1954
|
+
|
|
1955
|
+
lsp_count = run_lsp_integration(
|
|
1956
|
+
apm_package=apm_package,
|
|
1957
|
+
apm_modules_path=apm_modules_path,
|
|
1958
|
+
lock_path=_lock_path,
|
|
1959
|
+
existing_lock=_existing_lock,
|
|
1960
|
+
project_root=ctx.project_root,
|
|
1961
|
+
user_scope=(ctx.scope is InstallScope.USER),
|
|
1962
|
+
should_install=should_install_lsp,
|
|
1963
|
+
logger=logger,
|
|
1964
|
+
diagnostics=apm_diagnostics,
|
|
1965
|
+
target_context=(mcp_apm_config, ctx.target, ctx.scope),
|
|
1966
|
+
)
|
|
1967
|
+
|
|
1948
1968
|
# Local .apm/ content integration is now handled inside the
|
|
1949
1969
|
# install pipeline (phases/integrate.py + phases/post_deps_local.py,
|
|
1950
1970
|
# refactor F3). The duplicate target resolution, integrator
|
|
1951
1971
|
# initialization, and inline stale-cleanup block that lived here
|
|
1952
1972
|
# have been removed.
|
|
1953
1973
|
|
|
1954
|
-
return apm_count, mcp_count, apm_diagnostics
|
|
1974
|
+
return apm_count, mcp_count, lsp_count, apm_diagnostics
|
|
1955
1975
|
|
|
1956
1976
|
|
|
1957
1977
|
def _post_install_summary(
|
|
1958
|
-
*, logger, apm_count, mcp_count, apm_diagnostics, force, elapsed_seconds=None
|
|
1978
|
+
*, logger, apm_count, mcp_count, lsp_count=0, apm_diagnostics, force, elapsed_seconds=None
|
|
1959
1979
|
):
|
|
1960
1980
|
"""Thin shim forwarding to :func:`apm_cli.install.summary.render_post_install_summary`.
|
|
1961
1981
|
|
|
@@ -1969,6 +1989,7 @@ def _post_install_summary(
|
|
|
1969
1989
|
logger=logger,
|
|
1970
1990
|
apm_count=apm_count,
|
|
1971
1991
|
mcp_count=mcp_count,
|
|
1992
|
+
lsp_count=lsp_count,
|
|
1972
1993
|
apm_diagnostics=apm_diagnostics,
|
|
1973
1994
|
force=force,
|
|
1974
1995
|
elapsed_seconds=elapsed_seconds,
|
|
@@ -8,6 +8,7 @@ from __future__ import annotations
|
|
|
8
8
|
|
|
9
9
|
import builtins
|
|
10
10
|
import json
|
|
11
|
+
import logging
|
|
11
12
|
import re
|
|
12
13
|
import sys
|
|
13
14
|
import traceback
|
|
@@ -49,6 +50,8 @@ from ...marketplace.yml_schema import load_marketplace_yml
|
|
|
49
50
|
from ...utils.path_security import PathTraversalError, validate_path_segments
|
|
50
51
|
from .._helpers import _get_console, _is_interactive
|
|
51
52
|
|
|
53
|
+
logger = logging.getLogger(__name__)
|
|
54
|
+
|
|
52
55
|
# Restore builtins shadowed by subcommand names
|
|
53
56
|
list = builtins.list
|
|
54
57
|
|
|
@@ -83,6 +86,8 @@ class MarketplaceGroup(click.Group):
|
|
|
83
86
|
"init",
|
|
84
87
|
"check",
|
|
85
88
|
"outdated",
|
|
89
|
+
"audit",
|
|
90
|
+
"doctor",
|
|
86
91
|
"publish",
|
|
87
92
|
"package",
|
|
88
93
|
"migrate",
|
|
@@ -112,6 +117,8 @@ class MarketplaceGroup(click.Group):
|
|
|
112
117
|
cmd = self.get_command(ctx, name)
|
|
113
118
|
if cmd is None:
|
|
114
119
|
continue
|
|
120
|
+
if getattr(cmd, "hidden", False):
|
|
121
|
+
continue
|
|
115
122
|
help_text = cmd.get_short_help_str(limit=150)
|
|
116
123
|
commands.append((name, help_text))
|
|
117
124
|
if commands:
|
|
@@ -1025,15 +1032,36 @@ def _load_current_versions():
|
|
|
1025
1032
|
def _extract_tag_versions(refs, entry, yml, include_prerelease):
|
|
1026
1033
|
"""Extract (SemVer, tag_name) pairs from remote refs for a package entry."""
|
|
1027
1034
|
from ...marketplace._shared import iter_semver_tags
|
|
1028
|
-
from ...marketplace.tag_pattern import
|
|
1035
|
+
from ...marketplace.tag_pattern import (
|
|
1036
|
+
build_tag_regex,
|
|
1037
|
+
infer_tag_pattern_from_refs,
|
|
1038
|
+
)
|
|
1039
|
+
|
|
1040
|
+
def _collect(pattern: str) -> list:
|
|
1041
|
+
tag_rx = (
|
|
1042
|
+
build_tag_regex(pattern, name=entry.name)
|
|
1043
|
+
if "{name}" in pattern
|
|
1044
|
+
else build_tag_regex(pattern)
|
|
1045
|
+
)
|
|
1046
|
+
collected = []
|
|
1047
|
+
for sv, tag_name, _ in iter_semver_tags(refs, tag_rx):
|
|
1048
|
+
if sv.is_prerelease and not (include_prerelease or entry.include_prerelease):
|
|
1049
|
+
continue
|
|
1050
|
+
collected.append((sv, tag_name))
|
|
1051
|
+
return collected
|
|
1029
1052
|
|
|
1030
1053
|
pattern = entry.tag_pattern or yml.build.tag_pattern
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
if
|
|
1035
|
-
|
|
1036
|
-
|
|
1054
|
+
results = _collect(pattern)
|
|
1055
|
+
if not results:
|
|
1056
|
+
inferred = infer_tag_pattern_from_refs(refs, entry.name)
|
|
1057
|
+
if inferred and inferred != pattern:
|
|
1058
|
+
logger.debug(
|
|
1059
|
+
"Configured tag pattern %r matched no tags for %s; inferred %r",
|
|
1060
|
+
pattern,
|
|
1061
|
+
entry.name,
|
|
1062
|
+
inferred,
|
|
1063
|
+
)
|
|
1064
|
+
results = _collect(inferred)
|
|
1037
1065
|
return results
|
|
1038
1066
|
|
|
1039
1067
|
|
|
@@ -1506,6 +1534,7 @@ def search(expression, limit, verbose):
|
|
|
1506
1534
|
sys.exit(1)
|
|
1507
1535
|
|
|
1508
1536
|
|
|
1537
|
+
from .audit import audit # noqa: E402
|
|
1509
1538
|
from .check import check # noqa: E402
|
|
1510
1539
|
from .doctor import doctor # noqa: E402
|
|
1511
1540
|
from .init import init # noqa: E402
|
|
@@ -1546,6 +1575,7 @@ __all__ = [
|
|
|
1546
1575
|
"SemVer",
|
|
1547
1576
|
"TargetResult",
|
|
1548
1577
|
"add",
|
|
1578
|
+
"audit",
|
|
1549
1579
|
"browse",
|
|
1550
1580
|
"check",
|
|
1551
1581
|
"detect_config_source",
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
"""``apm marketplace audit`` command."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import sys
|
|
6
|
+
import traceback
|
|
7
|
+
|
|
8
|
+
import click
|
|
9
|
+
|
|
10
|
+
from ...core.command_logger import CommandLogger
|
|
11
|
+
from ...marketplace.audit import FetchStatus, run_audit
|
|
12
|
+
from ...marketplace.client import fetch_marketplace
|
|
13
|
+
from ...marketplace.registry import get_marketplace_by_name
|
|
14
|
+
from . import marketplace
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@marketplace.command(help="Check that plugin dependencies resolve through the marketplace")
|
|
18
|
+
@click.argument("name", required=True)
|
|
19
|
+
@click.option(
|
|
20
|
+
"--strict",
|
|
21
|
+
is_flag=True,
|
|
22
|
+
help="Exit non-zero when any plugin has bypass dependencies or fetch errors",
|
|
23
|
+
)
|
|
24
|
+
@click.option("--verbose", "-v", is_flag=True, help="Show detailed output")
|
|
25
|
+
def audit(name, strict, verbose):
|
|
26
|
+
"""Audit a registered marketplace's supply-chain pinning.
|
|
27
|
+
|
|
28
|
+
For each plugin in ``NAME``'s manifest, fetch the plugin's own
|
|
29
|
+
``apm.yml`` (at its pinned ref) and warn when ``dependencies.apm``
|
|
30
|
+
entries use direct repo paths, which bypass the marketplace's
|
|
31
|
+
version pinning and make transitive deps track HEAD.
|
|
32
|
+
"""
|
|
33
|
+
logger = CommandLogger("marketplace-audit", verbose=verbose)
|
|
34
|
+
try:
|
|
35
|
+
source = get_marketplace_by_name(name)
|
|
36
|
+
logger.start(f"Auditing marketplace '{name}'...", symbol="running")
|
|
37
|
+
|
|
38
|
+
manifest = fetch_marketplace(source, force_refresh=True)
|
|
39
|
+
n = len(manifest.plugins)
|
|
40
|
+
logger.progress(f"Checking {n} plugin{'' if n == 1 else 's'}...", symbol="info")
|
|
41
|
+
|
|
42
|
+
reports = run_audit(manifest, source)
|
|
43
|
+
|
|
44
|
+
ok_count = 0
|
|
45
|
+
bypass_total = 0
|
|
46
|
+
fetch_error_count = 0
|
|
47
|
+
skipped_count = 0
|
|
48
|
+
|
|
49
|
+
# Suppress the per-plugin section header when there is nothing to
|
|
50
|
+
# report and the user did not opt into verbose: in the all-clean
|
|
51
|
+
# default run the header would otherwise hang above an empty body.
|
|
52
|
+
has_findings = any(rep.fetch_status != FetchStatus.OK or rep.issues for rep in reports)
|
|
53
|
+
|
|
54
|
+
click.echo()
|
|
55
|
+
if has_findings or verbose:
|
|
56
|
+
click.echo("Audit Results:")
|
|
57
|
+
for rep in reports:
|
|
58
|
+
if rep.fetch_status == FetchStatus.OK:
|
|
59
|
+
if not rep.issues:
|
|
60
|
+
ok_count += 1
|
|
61
|
+
if verbose:
|
|
62
|
+
logger.success(
|
|
63
|
+
f" {rep.plugin_name}: deps are marketplace-resolved",
|
|
64
|
+
symbol="check",
|
|
65
|
+
)
|
|
66
|
+
continue
|
|
67
|
+
bypass_total += len(rep.issues)
|
|
68
|
+
if len(rep.issues) == 1:
|
|
69
|
+
verb_phrase = "1 dependency bypasses"
|
|
70
|
+
else:
|
|
71
|
+
verb_phrase = f"{len(rep.issues)} dependencies bypass"
|
|
72
|
+
logger.warning(
|
|
73
|
+
f" {rep.plugin_name}: {verb_phrase} the marketplace",
|
|
74
|
+
symbol="warning",
|
|
75
|
+
)
|
|
76
|
+
for issue in rep.issues:
|
|
77
|
+
click.echo(f" - '{issue.dep}'")
|
|
78
|
+
click.echo(f" hint: {issue.suggestion}")
|
|
79
|
+
elif rep.fetch_status in (
|
|
80
|
+
FetchStatus.NO_MANIFEST,
|
|
81
|
+
FetchStatus.UNSUPPORTED_SOURCE,
|
|
82
|
+
):
|
|
83
|
+
skipped_count += 1
|
|
84
|
+
if verbose:
|
|
85
|
+
logger.verbose_detail(
|
|
86
|
+
f" {rep.plugin_name}: skipped ({rep.fetch_status.value}"
|
|
87
|
+
f"{' - ' + rep.detail if rep.detail else ''})"
|
|
88
|
+
)
|
|
89
|
+
else:
|
|
90
|
+
fetch_error_count += 1
|
|
91
|
+
logger.warning(
|
|
92
|
+
f" {rep.plugin_name}: could not verify "
|
|
93
|
+
f"({rep.fetch_status.value}: {rep.detail})",
|
|
94
|
+
symbol="warning",
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
click.echo()
|
|
98
|
+
warn_noun = "warning" if bypass_total == 1 else "warnings"
|
|
99
|
+
err_noun = "error" if fetch_error_count == 1 else "errors"
|
|
100
|
+
logger.success(
|
|
101
|
+
f"Summary: {ok_count} clean, {bypass_total} bypass {warn_noun}, "
|
|
102
|
+
f"{skipped_count} skipped, "
|
|
103
|
+
f"{fetch_error_count} unverifiable {err_noun}",
|
|
104
|
+
symbol="check",
|
|
105
|
+
)
|
|
106
|
+
if bypass_total:
|
|
107
|
+
click.echo()
|
|
108
|
+
click.echo(
|
|
109
|
+
"Marketplace refs (name@marketplace) pin transitive deps "
|
|
110
|
+
"through the catalogue so consumers get the same versions "
|
|
111
|
+
"you tested. See: "
|
|
112
|
+
"https://microsoft.github.io/apm/reference/cli/marketplace/#apm-marketplace-audit-name"
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
if strict and (bypass_total or fetch_error_count):
|
|
116
|
+
sys.exit(1)
|
|
117
|
+
|
|
118
|
+
except Exception as e:
|
|
119
|
+
logger.error(f"Failed to audit marketplace: {e}")
|
|
120
|
+
logger.info("Run with --verbose for details.")
|
|
121
|
+
logger.verbose_detail(traceback.format_exc())
|
|
122
|
+
sys.exit(1)
|
|
@@ -16,12 +16,15 @@ from ..deps.outdated_row import OutdatedRow
|
|
|
16
16
|
|
|
17
17
|
logger = logging.getLogger(__name__)
|
|
18
18
|
|
|
19
|
+
# Fallback heuristic for _resolve_tag_pattern when inference misses plain tags.
|
|
19
20
|
TAG_RE = re.compile(r"^v?\d+\.\d+\.\d+")
|
|
20
21
|
|
|
21
22
|
|
|
22
|
-
def _is_tag_ref(ref: str) -> bool:
|
|
23
|
-
"""Return True when *ref*
|
|
24
|
-
|
|
23
|
+
def _is_tag_ref(ref: str, package_name: str | None = None) -> bool:
|
|
24
|
+
"""Return True when *ref* names a version tag (plain or patterned)."""
|
|
25
|
+
from ..marketplace.tag_pattern import is_version_tag_ref
|
|
26
|
+
|
|
27
|
+
return is_version_tag_ref(ref, package_name)
|
|
25
28
|
|
|
26
29
|
|
|
27
30
|
def _strip_v(ref: str) -> str:
|
|
@@ -29,6 +32,57 @@ def _strip_v(ref: str) -> str:
|
|
|
29
32
|
return ref[1:] if ref and ref.startswith("v") else (ref or "")
|
|
30
33
|
|
|
31
34
|
|
|
35
|
+
def _package_basename(dep) -> str:
|
|
36
|
+
"""Return the display name used in ``{name}`` tag patterns."""
|
|
37
|
+
if dep.marketplace_plugin_name:
|
|
38
|
+
return dep.marketplace_plugin_name
|
|
39
|
+
repo = dep.repo_url or ""
|
|
40
|
+
if not repo:
|
|
41
|
+
return ""
|
|
42
|
+
return repo.rstrip("/").split("/")[-1]
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def _resolve_tag_pattern(current_ref: str, package_name: str) -> str | None:
|
|
46
|
+
"""Return the tag pattern for *current_ref*, or ``None`` if not a version tag."""
|
|
47
|
+
from ..marketplace.tag_pattern import infer_tag_pattern
|
|
48
|
+
|
|
49
|
+
inferred = infer_tag_pattern(current_ref, package_name)
|
|
50
|
+
if inferred:
|
|
51
|
+
logger.debug(
|
|
52
|
+
"Resolved tag pattern %r for %s from ref %s",
|
|
53
|
+
inferred,
|
|
54
|
+
package_name,
|
|
55
|
+
current_ref,
|
|
56
|
+
)
|
|
57
|
+
return inferred
|
|
58
|
+
if TAG_RE.match(current_ref or ""):
|
|
59
|
+
return "v{version}" if (current_ref or "").startswith("v") else "{version}"
|
|
60
|
+
return None
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def _semver_tag_candidates(tag_refs, pattern: str, package_name: str = ""):
|
|
64
|
+
"""Return ``(SemVer, tag_name)`` pairs matching *pattern*, highest first."""
|
|
65
|
+
from ..marketplace.semver import SemVer, parse_semver
|
|
66
|
+
from ..marketplace.tag_pattern import build_tag_regex
|
|
67
|
+
|
|
68
|
+
tag_rx = (
|
|
69
|
+
build_tag_regex(pattern, name=package_name)
|
|
70
|
+
if "{name}" in pattern and package_name
|
|
71
|
+
else build_tag_regex(pattern)
|
|
72
|
+
)
|
|
73
|
+
candidates: list[tuple[SemVer, str]] = []
|
|
74
|
+
for remote_ref in tag_refs:
|
|
75
|
+
match = tag_rx.match(remote_ref.name)
|
|
76
|
+
if not match:
|
|
77
|
+
continue
|
|
78
|
+
version = match.group("version")
|
|
79
|
+
parsed = parse_semver(version)
|
|
80
|
+
if parsed is not None:
|
|
81
|
+
candidates.append((parsed, remote_ref.name))
|
|
82
|
+
candidates.sort(key=lambda pair: pair[0], reverse=True)
|
|
83
|
+
return candidates
|
|
84
|
+
|
|
85
|
+
|
|
32
86
|
def _find_remote_tip(ref_name, remote_refs):
|
|
33
87
|
"""Find the tip SHA for a branch ref from remote refs.
|
|
34
88
|
|
|
@@ -189,7 +243,9 @@ def _check_one_dep(dep, downloader, verbose, registry_ctx=None):
|
|
|
189
243
|
package=package_name, current=current_ref or "(none)", latest="-", status="unknown"
|
|
190
244
|
)
|
|
191
245
|
|
|
192
|
-
|
|
246
|
+
package_basename = _package_basename(dep)
|
|
247
|
+
tag_pattern = _resolve_tag_pattern(current_ref, package_basename)
|
|
248
|
+
is_tag = tag_pattern is not None
|
|
193
249
|
|
|
194
250
|
if is_tag:
|
|
195
251
|
tag_refs = [r for r in remote_refs if r.ref_type == GitReferenceType.TAG]
|
|
@@ -202,12 +258,28 @@ def _check_one_dep(dep, downloader, verbose, registry_ctx=None):
|
|
|
202
258
|
source="git tags",
|
|
203
259
|
)
|
|
204
260
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
261
|
+
from ..marketplace.tag_pattern import parse_tag_version
|
|
262
|
+
|
|
263
|
+
candidates = _semver_tag_candidates(tag_refs, tag_pattern, package_basename)
|
|
264
|
+
if not candidates:
|
|
265
|
+
return OutdatedRow(
|
|
266
|
+
package=package_name,
|
|
267
|
+
current=current_ref,
|
|
268
|
+
latest="-",
|
|
269
|
+
status="unknown",
|
|
270
|
+
source="git tags",
|
|
271
|
+
)
|
|
272
|
+
|
|
273
|
+
_, latest_tag = candidates[0]
|
|
274
|
+
current_ver = parse_tag_version(
|
|
275
|
+
current_ref, tag_pattern, name=package_basename
|
|
276
|
+
) or _strip_v(current_ref)
|
|
277
|
+
latest_ver = parse_tag_version(latest_tag, tag_pattern, name=package_basename) or _strip_v(
|
|
278
|
+
latest_tag
|
|
279
|
+
)
|
|
208
280
|
|
|
209
281
|
if is_newer_version(current_ver, latest_ver):
|
|
210
|
-
extra = [
|
|
282
|
+
extra = [name for _, name in candidates[:10]] if verbose else []
|
|
211
283
|
return OutdatedRow(
|
|
212
284
|
package=package_name,
|
|
213
285
|
current=current_ref,
|