apm-cli 0.12.4__tar.gz → 0.13.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.12.4 → apm_cli-0.13.0}/NOTICE +1 -1
- {apm_cli-0.12.4/src/apm_cli.egg-info → apm_cli-0.13.0}/PKG-INFO +4 -3
- {apm_cli-0.12.4 → apm_cli-0.13.0}/README.md +1 -1
- {apm_cli-0.12.4 → apm_cli-0.13.0}/pyproject.toml +14 -3
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/adapters/client/copilot.py +27 -16
- apm_cli-0.13.0/src/apm_cli/adapters/client/cursor.py +326 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/bundle/local_bundle.py +18 -1
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/bundle/plugin_exporter.py +8 -1
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/bundle/unpacker.py +16 -4
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/cache/git_cache.py +3 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/cli.py +2 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/commands/_apm_yml_writer.py +12 -1
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/commands/config.py +6 -1
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/commands/deps/cli.py +43 -34
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/commands/install.py +183 -114
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/commands/marketplace/__init__.py +39 -12
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/commands/outdated.py +1 -1
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/commands/pack.py +35 -6
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/commands/run.py +1 -1
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/commands/runtime.py +1 -1
- apm_cli-0.12.4/src/apm_cli/commands/update.py → apm_cli-0.13.0/src/apm_cli/commands/self_update.py +17 -8
- apm_cli-0.13.0/src/apm_cli/commands/update.py +319 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/core/apm_yml.py +10 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/core/auth.py +254 -88
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/core/command_logger.py +20 -2
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/core/errors.py +30 -4
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/core/token_manager.py +149 -7
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/deps/apm_resolver.py +83 -15
- apm_cli-0.13.0/src/apm_cli/deps/artifactory_orchestrator.py +319 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/deps/bare_cache.py +252 -7
- apm_cli-0.13.0/src/apm_cli/deps/clone_engine.py +342 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/deps/download_strategies.py +475 -126
- apm_cli-0.13.0/src/apm_cli/deps/git_auth_env.py +152 -0
- apm_cli-0.13.0/src/apm_cli/deps/git_reference_resolver.py +417 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/deps/github_downloader.py +196 -909
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/deps/github_downloader_validation.py +13 -0
- apm_cli-0.13.0/src/apm_cli/deps/host_backends.py +623 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/deps/package_validator.py +47 -1
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/deps/registry_proxy.py +14 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/deps/shared_clone_cache.py +75 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/install/errors.py +23 -0
- apm_cli-0.13.0/src/apm_cli/install/gitlab_resolver.py +41 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/install/local_bundle_handler.py +180 -3
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/install/mcp/command.py +5 -2
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/install/mcp/entry.py +5 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/install/mcp/writer.py +5 -0
- apm_cli-0.13.0/src/apm_cli/install/package_resolution.py +162 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/install/phases/targets.py +14 -4
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/install/pipeline.py +145 -35
- apm_cli-0.13.0/src/apm_cli/install/plan.py +425 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/install/request.py +15 -2
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/install/service.py +65 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/install/services.py +91 -9
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/install/validation.py +71 -33
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/integration/agent_integrator.py +20 -22
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/integration/base_integrator.py +9 -5
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/integration/prompt_integrator.py +3 -12
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/integration/targets.py +15 -5
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/marketplace/client.py +80 -48
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/marketplace/models.py +3 -3
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/marketplace/resolver.py +268 -21
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/models/apm_package.py +24 -4
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/models/dependency/reference.py +373 -47
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/policy/discovery.py +1 -1
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/policy/inheritance.py +7 -1
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/policy/parser.py +1 -1
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/policy/policy_checks.py +2 -2
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/policy/schema.py +6 -1
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/utils/console.py +3 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/utils/github_host.py +203 -13
- {apm_cli-0.12.4 → apm_cli-0.13.0/src/apm_cli.egg-info}/PKG-INFO +4 -3
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli.egg-info/SOURCES.txt +9 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli.egg-info/requires.txt +2 -1
- {apm_cli-0.12.4 → apm_cli-0.13.0}/tests/test_github_downloader.py +576 -30
- {apm_cli-0.12.4 → apm_cli-0.13.0}/tests/test_token_manager.py +270 -18
- apm_cli-0.12.4/src/apm_cli/adapters/client/cursor.py +0 -144
- {apm_cli-0.12.4 → apm_cli-0.13.0}/AUTHORS +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/LICENSE +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/setup.cfg +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/__init__.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/adapters/__init__.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/adapters/client/__init__.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/adapters/client/base.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/adapters/client/claude.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/adapters/client/codex.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/adapters/client/gemini.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/adapters/client/opencode.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/adapters/client/vscode.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/adapters/client/windsurf.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/adapters/package_manager/__init__.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/adapters/package_manager/base.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/adapters/package_manager/default_manager.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/bundle/__init__.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/bundle/lockfile_enrichment.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/bundle/packer.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/cache/__init__.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/cache/http_cache.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/cache/integrity.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/cache/locking.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/cache/paths.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/cache/url_normalize.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/commands/__init__.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/commands/_helpers.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/commands/audit.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/commands/cache.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/commands/compile/__init__.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/commands/compile/cli.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/commands/compile/watcher.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/commands/deps/__init__.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/commands/deps/_utils.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/commands/experimental.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/commands/init.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/commands/list_cmd.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/commands/marketplace/check.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/commands/marketplace/doctor.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/commands/marketplace/init.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/commands/marketplace/migrate.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/commands/marketplace/outdated.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/commands/marketplace/plugin/__init__.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/commands/marketplace/plugin/add.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/commands/marketplace/plugin/remove.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/commands/marketplace/plugin/set.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/commands/marketplace/publish.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/commands/marketplace/validate.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/commands/mcp.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/commands/policy.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/commands/prune.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/commands/targets.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/commands/uninstall/__init__.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/commands/uninstall/cli.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/commands/uninstall/engine.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/commands/view.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/compilation/__init__.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/compilation/agents_compiler.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/compilation/build_id.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/compilation/claude_formatter.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/compilation/constants.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/compilation/constitution.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/compilation/constitution_block.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/compilation/context_optimizer.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/compilation/distributed_compiler.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/compilation/gemini_formatter.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/compilation/injector.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/compilation/link_resolver.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/compilation/output_writer.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/compilation/template_builder.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/config.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/constants.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/core/__init__.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/core/azure_cli.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/core/build_orchestrator.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/core/conflict_detector.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/core/docker_args.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/core/experimental.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/core/null_logger.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/core/operations.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/core/safe_installer.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/core/scope.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/core/script_runner.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/core/target_detection.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/deps/__init__.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/deps/aggregator.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/deps/artifactory_entry.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/deps/dependency_graph.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/deps/git_remote_ops.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/deps/installed_package.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/deps/lockfile.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/deps/plugin_parser.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/deps/transport_selection.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/deps/verifier.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/drift.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/factory.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/install/__init__.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/install/cache_pin.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/install/context.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/install/drift.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/install/heals/__init__.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/install/heals/base.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/install/heals/branch_ref_drift.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/install/heals/buggy_lockfile_recovery.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/install/helpers/__init__.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/install/helpers/security_scan.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/install/insecure_policy.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/install/mcp/__init__.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/install/mcp/args.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/install/mcp/conflicts.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/install/mcp/registry.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/install/mcp/warnings.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/install/phases/__init__.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/install/phases/cleanup.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/install/phases/download.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/install/phases/finalize.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/install/phases/heal.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/install/phases/integrate.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/install/phases/local_content.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/install/phases/lockfile.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/install/phases/policy_gate.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/install/phases/policy_target_check.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/install/phases/post_deps_local.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/install/phases/resolve.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/install/presentation/__init__.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/install/presentation/dry_run.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/install/skill_path_migration.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/install/sources.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/install/summary.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/install/template.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/integration/__init__.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/integration/cleanup.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/integration/command_integrator.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/integration/copilot_cowork_paths.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/integration/coverage.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/integration/dispatch.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/integration/hook_integrator.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/integration/instruction_integrator.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/integration/mcp_integrator.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/integration/skill_integrator.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/integration/skill_transformer.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/integration/utils.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/marketplace/__init__.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/marketplace/_git_utils.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/marketplace/_io.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/marketplace/builder.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/marketplace/errors.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/marketplace/git_stderr.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/marketplace/init_template.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/marketplace/migration.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/marketplace/pr_integration.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/marketplace/publisher.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/marketplace/ref_resolver.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/marketplace/registry.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/marketplace/semver.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/marketplace/shadow_detector.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/marketplace/tag_pattern.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/marketplace/validator.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/marketplace/version_pins.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/marketplace/yml_editor.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/marketplace/yml_schema.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/models/__init__.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/models/dependency/__init__.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/models/dependency/mcp.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/models/dependency/types.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/models/plugin.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/models/results.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/models/validation.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/output/__init__.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/output/formatters.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/output/models.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/output/script_formatters.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/policy/__init__.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/policy/_help_text.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/policy/ci_checks.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/policy/install_preflight.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/policy/matcher.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/policy/models.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/policy/outcome_routing.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/policy/project_config.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/primitives/__init__.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/primitives/discovery.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/primitives/models.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/primitives/parser.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/registry/__init__.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/registry/client.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/registry/integration.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/registry/operations.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/runtime/__init__.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/runtime/base.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/runtime/codex_runtime.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/runtime/copilot_runtime.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/runtime/factory.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/runtime/llm_runtime.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/runtime/manager.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/security/__init__.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/security/audit_report.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/security/content_scanner.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/security/file_scanner.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/security/gate.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/update_policy.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/utils/__init__.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/utils/atomic_io.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/utils/content_hash.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/utils/diagnostics.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/utils/exclude.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/utils/file_ops.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/utils/git_env.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/utils/guards.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/utils/helpers.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/utils/install_tui.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/utils/normalization.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/utils/path_security.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/utils/paths.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/utils/reflink.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/utils/short_sha.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/utils/subprocess_env.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/utils/version_checker.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/utils/yaml_io.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/version.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/workflow/__init__.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/workflow/discovery.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/workflow/parser.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli/workflow/runner.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli.egg-info/dependency_links.txt +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli.egg-info/entry_points.txt +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/src/apm_cli.egg-info/top_level.txt +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/tests/test_apm_package_models.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/tests/test_apm_resolver.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/tests/test_codex_docker_args_fix.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/tests/test_codex_empty_string_and_defaults.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/tests/test_collision_integration.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/tests/test_console.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/tests/test_distributed_compilation.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/tests/test_empty_string_and_defaults.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/tests/test_enhanced_discovery.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/tests/test_github_downloader_token_precedence.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/tests/test_lockfile.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/tests/test_runnable_prompts.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/tests/test_runtime_manager_token_precedence.py +0 -0
- {apm_cli-0.12.4 → apm_cli-0.13.0}/tests/test_virtual_package_multi_install.py +0 -0
|
@@ -139,7 +139,7 @@ SOFTWARE.
|
|
|
139
139
|
|
|
140
140
|
## Component. requests
|
|
141
141
|
|
|
142
|
-
- Version requirement: `>=2.
|
|
142
|
+
- Version requirement: `>=2.31.0`
|
|
143
143
|
- Upstream: https://github.com/psf/requests
|
|
144
144
|
- SPDX: `Apache-2.0`
|
|
145
145
|
- Notes: Apache-2.0 requires forwarding the upstream NOTICE file verbatim.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: apm-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.13.0
|
|
4
4
|
Summary: MCP configuration tool
|
|
5
5
|
Author-email: Daniel Meppiel <user@example.com>
|
|
6
6
|
License: MIT License
|
|
@@ -40,7 +40,7 @@ License-File: AUTHORS
|
|
|
40
40
|
Requires-Dist: click>=8.0.0
|
|
41
41
|
Requires-Dist: colorama>=0.4.6
|
|
42
42
|
Requires-Dist: pyyaml>=6.0.0
|
|
43
|
-
Requires-Dist: requests>=2.
|
|
43
|
+
Requires-Dist: requests>=2.31.0
|
|
44
44
|
Requires-Dist: python-frontmatter>=1.0.0
|
|
45
45
|
Requires-Dist: llm>=0.17.0
|
|
46
46
|
Requires-Dist: llm-github-models>=0.1.0
|
|
@@ -56,6 +56,7 @@ Provides-Extra: dev
|
|
|
56
56
|
Requires-Dist: pytest>=7.0.0; extra == "dev"
|
|
57
57
|
Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
|
|
58
58
|
Requires-Dist: pytest-xdist>=3.0.0; extra == "dev"
|
|
59
|
+
Requires-Dist: pytest-split>=0.9.0; extra == "dev"
|
|
59
60
|
Requires-Dist: ruff>=0.11.0; extra == "dev"
|
|
60
61
|
Requires-Dist: mypy>=1.0.0; extra == "dev"
|
|
61
62
|
Requires-Dist: jsonschema>=4.0.0; extra == "dev"
|
|
@@ -133,7 +134,7 @@ One command, no configuration -- VS Code and GitHub Copilot read the file automa
|
|
|
133
134
|
One `apm.yml` describes every primitive your agents need — instructions, skills, prompts, agents, hooks, plugins, MCP servers — and `apm install` reproduces the exact same setup across every client on every machine. `apm.lock.yaml` pins the resolved tree the way `package-lock.json` does for npm.
|
|
134
135
|
|
|
135
136
|
- **[One manifest for everything](https://microsoft.github.io/apm/reference/primitive-types/)** — declared once, deployed across Copilot, Claude, Cursor, OpenCode, Codex, Gemini, Windsurf
|
|
136
|
-
- **[Install from anywhere](https://microsoft.github.io/apm/guides/dependencies/)** — GitHub, GitLab, Bitbucket, Azure DevOps, GitHub Enterprise, any git host
|
|
137
|
+
- **[Install from anywhere](https://microsoft.github.io/apm/guides/dependencies/)** — GitHub, GitLab, Bitbucket, Azure DevOps, GitHub Enterprise, Gitea, Gogs, any git host
|
|
137
138
|
- **[Transitive dependencies](https://microsoft.github.io/apm/guides/dependencies/)** — packages can depend on packages; APM resolves the full tree
|
|
138
139
|
- **[Author plugins](https://microsoft.github.io/apm/guides/plugins/)** — build Copilot, Claude, and Cursor plugins with dependency management, then export standard `plugin.json`
|
|
139
140
|
- **[Marketplaces](https://microsoft.github.io/apm/guides/marketplaces/)** — install plugins from curated registries in one command, deployed across all targets and locked
|
|
@@ -68,7 +68,7 @@ One command, no configuration -- VS Code and GitHub Copilot read the file automa
|
|
|
68
68
|
One `apm.yml` describes every primitive your agents need — instructions, skills, prompts, agents, hooks, plugins, MCP servers — and `apm install` reproduces the exact same setup across every client on every machine. `apm.lock.yaml` pins the resolved tree the way `package-lock.json` does for npm.
|
|
69
69
|
|
|
70
70
|
- **[One manifest for everything](https://microsoft.github.io/apm/reference/primitive-types/)** — declared once, deployed across Copilot, Claude, Cursor, OpenCode, Codex, Gemini, Windsurf
|
|
71
|
-
- **[Install from anywhere](https://microsoft.github.io/apm/guides/dependencies/)** — GitHub, GitLab, Bitbucket, Azure DevOps, GitHub Enterprise, any git host
|
|
71
|
+
- **[Install from anywhere](https://microsoft.github.io/apm/guides/dependencies/)** — GitHub, GitLab, Bitbucket, Azure DevOps, GitHub Enterprise, Gitea, Gogs, any git host
|
|
72
72
|
- **[Transitive dependencies](https://microsoft.github.io/apm/guides/dependencies/)** — packages can depend on packages; APM resolves the full tree
|
|
73
73
|
- **[Author plugins](https://microsoft.github.io/apm/guides/plugins/)** — build Copilot, Claude, and Cursor plugins with dependency management, then export standard `plugin.json`
|
|
74
74
|
- **[Marketplaces](https://microsoft.github.io/apm/guides/marketplaces/)** — install plugins from curated registries in one command, deployed across all targets and locked
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "apm-cli"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.13.0"
|
|
8
8
|
description = "MCP configuration tool"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
@@ -25,7 +25,7 @@ dependencies = [
|
|
|
25
25
|
"click>=8.0.0",
|
|
26
26
|
"colorama>=0.4.6",
|
|
27
27
|
"pyyaml>=6.0.0",
|
|
28
|
-
"requests>=2.
|
|
28
|
+
"requests>=2.31.0",
|
|
29
29
|
"python-frontmatter>=1.0.0",
|
|
30
30
|
"llm>=0.17.0",
|
|
31
31
|
"llm-github-models>=0.1.0",
|
|
@@ -44,6 +44,7 @@ dev = [
|
|
|
44
44
|
"pytest>=7.0.0",
|
|
45
45
|
"pytest-cov>=4.0.0",
|
|
46
46
|
"pytest-xdist>=3.0.0",
|
|
47
|
+
"pytest-split>=0.9.0",
|
|
47
48
|
"ruff>=0.11.0",
|
|
48
49
|
"mypy>=1.0.0",
|
|
49
50
|
"jsonschema>=4.0.0",
|
|
@@ -128,11 +129,21 @@ warn_return_any = true
|
|
|
128
129
|
warn_unused_configs = true
|
|
129
130
|
|
|
130
131
|
[tool.pytest.ini_options]
|
|
131
|
-
addopts = "-m 'not benchmark'"
|
|
132
|
+
addopts = "-m 'not benchmark and not live'"
|
|
132
133
|
markers = [
|
|
133
134
|
"integration: marks tests as integration tests that may require network access",
|
|
134
135
|
"live: marks tests that hit real GitHub repos (requires network + optional GITHUB_TOKEN)",
|
|
135
136
|
"slow: marks tests as slow running tests",
|
|
136
137
|
"benchmark: marks performance benchmark tests (deselected by default, run with -m benchmark)",
|
|
138
|
+
"requires_e2e_mode: requires APM_E2E_TESTS=1",
|
|
139
|
+
"requires_github_token: requires GITHUB_APM_PAT or GITHUB_TOKEN",
|
|
140
|
+
"requires_ado_pat: requires ADO_APM_PAT",
|
|
141
|
+
"requires_ado_bearer: requires az CLI logged in + APM_TEST_ADO_BEARER=1",
|
|
142
|
+
"requires_network_integration: requires APM_RUN_INTEGRATION_TESTS=1",
|
|
143
|
+
"requires_apm_binary: requires built apm binary on PATH (or APM_BINARY_PATH)",
|
|
144
|
+
"requires_runtime_codex: requires codex runtime installed",
|
|
145
|
+
"requires_runtime_copilot: requires GitHub Copilot CLI runtime installed",
|
|
146
|
+
"requires_runtime_llm: requires llm runtime installed",
|
|
147
|
+
"requires_inference: requires APM_RUN_INFERENCE_TESTS=1 + model API access",
|
|
137
148
|
]
|
|
138
149
|
|
|
@@ -1208,8 +1208,10 @@ class CopilotClientAdapter(MCPClientAdapter):
|
|
|
1208
1208
|
def _is_github_server(self, server_name, url):
|
|
1209
1209
|
"""Securely determine if a server is a GitHub MCP server.
|
|
1210
1210
|
|
|
1211
|
-
|
|
1212
|
-
|
|
1211
|
+
Uses proper URL parsing and hostname validation to prevent token
|
|
1212
|
+
injection via poisoned registry entries. Both the server name and
|
|
1213
|
+
the URL hostname must match the GitHub allowlists before a GitHub
|
|
1214
|
+
token is injected.
|
|
1213
1215
|
|
|
1214
1216
|
Args:
|
|
1215
1217
|
server_name (str): Name of the MCP server.
|
|
@@ -1220,7 +1222,6 @@ class CopilotClientAdapter(MCPClientAdapter):
|
|
|
1220
1222
|
"""
|
|
1221
1223
|
from urllib.parse import urlparse
|
|
1222
1224
|
|
|
1223
|
-
# Check server name against an allowlist of known GitHub MCP servers
|
|
1224
1225
|
github_server_names = [
|
|
1225
1226
|
"github-mcp-server",
|
|
1226
1227
|
"github",
|
|
@@ -1228,23 +1229,33 @@ class CopilotClientAdapter(MCPClientAdapter):
|
|
|
1228
1229
|
"github-copilot-mcp-server",
|
|
1229
1230
|
]
|
|
1230
1231
|
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1232
|
+
def _is_github_mcp_hostname(hostname: str) -> bool:
|
|
1233
|
+
"""Check if *hostname* belongs to GitHub (cloud, enterprise, or Copilot API)."""
|
|
1234
|
+
if is_github_hostname(hostname):
|
|
1235
|
+
return True
|
|
1236
|
+
h = hostname.lower()
|
|
1237
|
+
# Subdomains of github.com (e.g. api.github.com)
|
|
1238
|
+
if h.endswith(".github.com"):
|
|
1239
|
+
return True
|
|
1240
|
+
# Copilot API hosts (e.g. api.githubcopilot.com, api.business.githubcopilot.com)
|
|
1241
|
+
return h == "githubcopilot.com" or h.endswith(".githubcopilot.com")
|
|
1242
|
+
|
|
1243
|
+
name_matches = bool(
|
|
1244
|
+
server_name and server_name.lower() in [n.lower() for n in github_server_names]
|
|
1245
|
+
)
|
|
1246
|
+
|
|
1247
|
+
# Parse and validate hostname from URL
|
|
1248
|
+
hostname = None
|
|
1236
1249
|
if url:
|
|
1237
1250
|
try:
|
|
1238
1251
|
parsed_url = urlparse(url)
|
|
1252
|
+
# Reject non-HTTPS URLs to prevent cleartext token leakage
|
|
1253
|
+
if parsed_url.scheme and parsed_url.scheme.lower() != "https":
|
|
1254
|
+
return False
|
|
1239
1255
|
hostname = parsed_url.hostname
|
|
1240
|
-
|
|
1241
|
-
if hostname:
|
|
1242
|
-
# Use helper to determine whether hostname is a GitHub host (cloud or enterprise)
|
|
1243
|
-
if is_github_hostname(hostname):
|
|
1244
|
-
return True
|
|
1245
|
-
|
|
1246
1256
|
except Exception:
|
|
1247
|
-
# If URL parsing fails, assume it's not a GitHub server
|
|
1248
1257
|
return False
|
|
1249
1258
|
|
|
1250
|
-
|
|
1259
|
+
host_matches = bool(hostname and _is_github_mcp_hostname(hostname))
|
|
1260
|
+
|
|
1261
|
+
return name_matches and host_matches
|
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
"""Cursor IDE implementation of MCP client adapter.
|
|
2
|
+
|
|
3
|
+
Cursor uses the standard ``mcpServers`` JSON format at ``.cursor/mcp.json``
|
|
4
|
+
(repo-local). Unlike the Copilot adapter, this adapter emits Cursor-native
|
|
5
|
+
transport discriminators (``type: stdio`` / ``type: http``) and omits
|
|
6
|
+
Copilot-only fields (``tools``, ``id``).
|
|
7
|
+
|
|
8
|
+
APM only writes to ``.cursor/mcp.json`` when the ``.cursor/`` directory
|
|
9
|
+
already exists -- Cursor support is opt-in.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import json
|
|
13
|
+
import os
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
|
|
16
|
+
from ...core.docker_args import DockerArgsProcessor
|
|
17
|
+
from ...core.token_manager import GitHubTokenManager
|
|
18
|
+
from .copilot import CopilotClientAdapter
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class CursorClientAdapter(CopilotClientAdapter):
|
|
22
|
+
"""Cursor IDE MCP client adapter.
|
|
23
|
+
|
|
24
|
+
Inherits config-path and read/write logic from
|
|
25
|
+
:class:`CopilotClientAdapter` but overrides ``_format_server_config`` to
|
|
26
|
+
emit Cursor-native transport discriminators instead of Copilot-only fields.
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
supports_user_scope: bool = False
|
|
30
|
+
target_name: str = "cursor"
|
|
31
|
+
mcp_servers_key: str = "mcpServers"
|
|
32
|
+
|
|
33
|
+
# Cursor's mcp.json runtime-substitution support has not yet been
|
|
34
|
+
# individually audited (see #1152). Pin to the legacy install-time
|
|
35
|
+
# resolution behaviour so this adapter is unchanged by the Copilot
|
|
36
|
+
# security fix; revisit in a follow-up.
|
|
37
|
+
_supports_runtime_env_substitution: bool = False
|
|
38
|
+
|
|
39
|
+
# ------------------------------------------------------------------ #
|
|
40
|
+
# Config path
|
|
41
|
+
# ------------------------------------------------------------------ #
|
|
42
|
+
|
|
43
|
+
def get_config_path(self):
|
|
44
|
+
"""Return the path to ``.cursor/mcp.json`` in the repository root.
|
|
45
|
+
|
|
46
|
+
Unlike the Copilot adapter this is a **repo-local** path. The
|
|
47
|
+
``.cursor/`` directory is *not* created automatically -- APM only
|
|
48
|
+
writes here when the directory already exists.
|
|
49
|
+
"""
|
|
50
|
+
cursor_dir = self.project_root / ".cursor"
|
|
51
|
+
return str(cursor_dir / "mcp.json")
|
|
52
|
+
|
|
53
|
+
# ------------------------------------------------------------------ #
|
|
54
|
+
# Config read / write -- override to avoid auto-creating the directory
|
|
55
|
+
# ------------------------------------------------------------------ #
|
|
56
|
+
|
|
57
|
+
def update_config(self, config_updates):
|
|
58
|
+
"""Merge *config_updates* into the ``mcpServers`` section.
|
|
59
|
+
|
|
60
|
+
The ``.cursor/`` directory must already exist; if it does not, this
|
|
61
|
+
method returns silently (opt-in behaviour).
|
|
62
|
+
"""
|
|
63
|
+
config_path = Path(self.get_config_path())
|
|
64
|
+
|
|
65
|
+
# Opt-in: only write when .cursor/ already exists
|
|
66
|
+
if not config_path.parent.exists():
|
|
67
|
+
return
|
|
68
|
+
|
|
69
|
+
current_config = self.get_current_config()
|
|
70
|
+
if "mcpServers" not in current_config:
|
|
71
|
+
current_config["mcpServers"] = {}
|
|
72
|
+
|
|
73
|
+
current_config["mcpServers"].update(config_updates)
|
|
74
|
+
|
|
75
|
+
with open(config_path, "w", encoding="utf-8") as f:
|
|
76
|
+
json.dump(current_config, f, indent=2)
|
|
77
|
+
|
|
78
|
+
def get_current_config(self):
|
|
79
|
+
"""Read the current ``.cursor/mcp.json`` contents."""
|
|
80
|
+
config_path = self.get_config_path()
|
|
81
|
+
|
|
82
|
+
if not os.path.exists(config_path):
|
|
83
|
+
return {}
|
|
84
|
+
|
|
85
|
+
try:
|
|
86
|
+
with open(config_path, encoding="utf-8") as f:
|
|
87
|
+
return json.load(f)
|
|
88
|
+
except (OSError, json.JSONDecodeError):
|
|
89
|
+
return {}
|
|
90
|
+
|
|
91
|
+
# ------------------------------------------------------------------ #
|
|
92
|
+
# _format_server_config -- Cursor-native schema
|
|
93
|
+
# ------------------------------------------------------------------ #
|
|
94
|
+
|
|
95
|
+
def _format_server_config(self, server_info, env_overrides=None, runtime_vars=None):
|
|
96
|
+
"""Format server info into Cursor MCP configuration.
|
|
97
|
+
|
|
98
|
+
Cursor uses a transport discriminator field ``type`` to determine how
|
|
99
|
+
to launch an MCP server:
|
|
100
|
+
|
|
101
|
+
- ``"type": "stdio"`` for local process servers (raw stdio or packages)
|
|
102
|
+
- ``"type": "http"`` for remote HTTP/SSE servers
|
|
103
|
+
|
|
104
|
+
Copilot-only fields ``tools`` and ``id`` are never emitted.
|
|
105
|
+
|
|
106
|
+
Args:
|
|
107
|
+
server_info: Server information from registry.
|
|
108
|
+
env_overrides: Pre-collected environment variable overrides.
|
|
109
|
+
runtime_vars: Pre-collected runtime variable values.
|
|
110
|
+
|
|
111
|
+
Returns:
|
|
112
|
+
dict suitable for writing to ``.cursor/mcp.json``.
|
|
113
|
+
"""
|
|
114
|
+
if runtime_vars is None:
|
|
115
|
+
runtime_vars = {}
|
|
116
|
+
|
|
117
|
+
config: dict = {}
|
|
118
|
+
|
|
119
|
+
# --- raw stdio (self-defined deps) ---
|
|
120
|
+
raw = server_info.get("_raw_stdio")
|
|
121
|
+
if raw:
|
|
122
|
+
config["type"] = "stdio"
|
|
123
|
+
config["command"] = raw["command"]
|
|
124
|
+
resolved_env_for_args: dict = {}
|
|
125
|
+
if raw.get("env"):
|
|
126
|
+
resolved_env_for_args = self._resolve_environment_variables(
|
|
127
|
+
raw["env"], env_overrides=env_overrides
|
|
128
|
+
)
|
|
129
|
+
config["env"] = resolved_env_for_args
|
|
130
|
+
self._warn_input_variables(raw["env"], server_info.get("name", ""), "Cursor")
|
|
131
|
+
args = raw.get("args") or []
|
|
132
|
+
config["args"] = [
|
|
133
|
+
self._resolve_variable_placeholders(arg, resolved_env_for_args, runtime_vars)
|
|
134
|
+
if isinstance(arg, str)
|
|
135
|
+
else arg
|
|
136
|
+
for arg in args
|
|
137
|
+
]
|
|
138
|
+
return config
|
|
139
|
+
|
|
140
|
+
# --- remote endpoints ---
|
|
141
|
+
remotes = server_info.get("remotes", [])
|
|
142
|
+
if remotes:
|
|
143
|
+
remote = self._select_remote_with_url(remotes) or remotes[0]
|
|
144
|
+
|
|
145
|
+
transport = (remote.get("transport_type") or "").strip()
|
|
146
|
+
if not transport:
|
|
147
|
+
transport = "http"
|
|
148
|
+
elif transport not in ("sse", "http", "streamable-http"):
|
|
149
|
+
raise ValueError(
|
|
150
|
+
f"Unsupported remote transport '{transport}' for Cursor. "
|
|
151
|
+
f"Server: {server_info.get('name', 'unknown')}. "
|
|
152
|
+
f"Supported transports: http, sse, streamable-http."
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
config["type"] = "http"
|
|
156
|
+
config["url"] = (remote.get("url") or "").strip()
|
|
157
|
+
|
|
158
|
+
# Add authentication headers for GitHub MCP server
|
|
159
|
+
server_name = server_info.get("name", "")
|
|
160
|
+
is_github_server = self._is_github_server(server_name, remote.get("url", ""))
|
|
161
|
+
|
|
162
|
+
if is_github_server:
|
|
163
|
+
_tm = GitHubTokenManager()
|
|
164
|
+
github_token = _tm.get_token_for_purpose("copilot") or os.getenv(
|
|
165
|
+
"GITHUB_PERSONAL_ACCESS_TOKEN"
|
|
166
|
+
)
|
|
167
|
+
if github_token:
|
|
168
|
+
config["headers"] = {"Authorization": f"Bearer {github_token}"}
|
|
169
|
+
|
|
170
|
+
# Add any additional headers from registry if present
|
|
171
|
+
headers = remote.get("headers", [])
|
|
172
|
+
if headers:
|
|
173
|
+
if "headers" not in config:
|
|
174
|
+
config["headers"] = {}
|
|
175
|
+
for header in headers:
|
|
176
|
+
header_name = header.get("name", "")
|
|
177
|
+
header_value = header.get("value", "")
|
|
178
|
+
if header_name and header_value:
|
|
179
|
+
# Prevent registry-supplied headers from overriding
|
|
180
|
+
# the injected GitHub token
|
|
181
|
+
if header_name == "Authorization" and is_github_server:
|
|
182
|
+
continue
|
|
183
|
+
resolved_value = self._resolve_env_variable(
|
|
184
|
+
header_name, header_value, env_overrides
|
|
185
|
+
)
|
|
186
|
+
config["headers"][header_name] = resolved_value
|
|
187
|
+
|
|
188
|
+
# Warn about unresolvable ${input:...} references in headers
|
|
189
|
+
if config.get("headers"):
|
|
190
|
+
self._warn_input_variables(config["headers"], server_info.get("name", ""), "Cursor")
|
|
191
|
+
|
|
192
|
+
return config
|
|
193
|
+
|
|
194
|
+
# --- local packages ---
|
|
195
|
+
packages = server_info.get("packages", [])
|
|
196
|
+
|
|
197
|
+
if not packages and not remotes:
|
|
198
|
+
raise ValueError(
|
|
199
|
+
f"MCP server has incomplete configuration in registry - "
|
|
200
|
+
f"no package information or remote endpoints available. "
|
|
201
|
+
f"This appears to be a temporary registry issue. "
|
|
202
|
+
f"Server: {server_info.get('name', 'unknown')}"
|
|
203
|
+
)
|
|
204
|
+
|
|
205
|
+
if packages:
|
|
206
|
+
package = self._select_best_package(packages)
|
|
207
|
+
|
|
208
|
+
if package:
|
|
209
|
+
registry_name = self._infer_registry_name(package)
|
|
210
|
+
package_name = package.get("name", "")
|
|
211
|
+
runtime_hint = package.get("runtime_hint", "")
|
|
212
|
+
runtime_arguments = package.get("runtime_arguments", [])
|
|
213
|
+
package_arguments = package.get("package_arguments", [])
|
|
214
|
+
env_vars = package.get("environment_variables", [])
|
|
215
|
+
|
|
216
|
+
resolved_env = self._resolve_environment_variables(env_vars, env_overrides)
|
|
217
|
+
processed_runtime_args = self._process_arguments(
|
|
218
|
+
runtime_arguments, resolved_env, runtime_vars
|
|
219
|
+
)
|
|
220
|
+
processed_package_args = self._process_arguments(
|
|
221
|
+
package_arguments, resolved_env, runtime_vars
|
|
222
|
+
)
|
|
223
|
+
|
|
224
|
+
config["type"] = "stdio"
|
|
225
|
+
|
|
226
|
+
if registry_name == "npm":
|
|
227
|
+
config["command"] = runtime_hint or "npx"
|
|
228
|
+
config["args"] = (
|
|
229
|
+
["-y", package_name] + processed_runtime_args + processed_package_args # noqa: RUF005
|
|
230
|
+
)
|
|
231
|
+
if resolved_env:
|
|
232
|
+
config["env"] = resolved_env
|
|
233
|
+
elif registry_name == "docker":
|
|
234
|
+
config["command"] = "docker"
|
|
235
|
+
if processed_runtime_args:
|
|
236
|
+
config["args"] = self._inject_env_vars_into_docker_args(
|
|
237
|
+
processed_runtime_args, resolved_env
|
|
238
|
+
)
|
|
239
|
+
else:
|
|
240
|
+
config["args"] = DockerArgsProcessor.process_docker_args(
|
|
241
|
+
["run", "-i", "--rm", package_name], resolved_env
|
|
242
|
+
)
|
|
243
|
+
elif registry_name == "pypi":
|
|
244
|
+
config["command"] = runtime_hint or "uvx"
|
|
245
|
+
config["args"] = (
|
|
246
|
+
[package_name] + processed_runtime_args + processed_package_args # noqa: RUF005
|
|
247
|
+
)
|
|
248
|
+
if resolved_env:
|
|
249
|
+
config["env"] = resolved_env
|
|
250
|
+
elif registry_name == "homebrew":
|
|
251
|
+
config["command"] = (
|
|
252
|
+
package_name.split("/")[-1] if "/" in package_name else package_name
|
|
253
|
+
)
|
|
254
|
+
config["args"] = processed_runtime_args + processed_package_args
|
|
255
|
+
if resolved_env:
|
|
256
|
+
config["env"] = resolved_env
|
|
257
|
+
else:
|
|
258
|
+
config["command"] = runtime_hint or package_name
|
|
259
|
+
config["args"] = processed_runtime_args + processed_package_args
|
|
260
|
+
if resolved_env:
|
|
261
|
+
config["env"] = resolved_env
|
|
262
|
+
else:
|
|
263
|
+
raise ValueError(
|
|
264
|
+
f"No supported package type found for Cursor. "
|
|
265
|
+
f"Server: {server_info.get('name', 'unknown')}. "
|
|
266
|
+
f"Available packages: "
|
|
267
|
+
f"{[p.get('registry_name', 'unknown') for p in packages]}."
|
|
268
|
+
)
|
|
269
|
+
|
|
270
|
+
return config
|
|
271
|
+
|
|
272
|
+
# ------------------------------------------------------------------ #
|
|
273
|
+
# configure_mcp_server -- thin override for the print label
|
|
274
|
+
# ------------------------------------------------------------------ #
|
|
275
|
+
|
|
276
|
+
def configure_mcp_server(
|
|
277
|
+
self,
|
|
278
|
+
server_url,
|
|
279
|
+
server_name=None,
|
|
280
|
+
enabled=True,
|
|
281
|
+
env_overrides=None,
|
|
282
|
+
server_info_cache=None,
|
|
283
|
+
runtime_vars=None,
|
|
284
|
+
):
|
|
285
|
+
"""Configure an MCP server in Cursor's ``.cursor/mcp.json``.
|
|
286
|
+
|
|
287
|
+
Delegates entirely to the parent implementation but prints a
|
|
288
|
+
Cursor-specific success message.
|
|
289
|
+
"""
|
|
290
|
+
if not server_url:
|
|
291
|
+
print("Error: server_url cannot be empty")
|
|
292
|
+
return False
|
|
293
|
+
|
|
294
|
+
# Opt-in: skip silently when .cursor/ does not exist
|
|
295
|
+
cursor_dir = self.project_root / ".cursor"
|
|
296
|
+
if not cursor_dir.exists():
|
|
297
|
+
return True # nothing to do, not an error
|
|
298
|
+
|
|
299
|
+
try:
|
|
300
|
+
# Use cached server info if available, otherwise fetch from registry
|
|
301
|
+
if server_info_cache and server_url in server_info_cache:
|
|
302
|
+
server_info = server_info_cache[server_url]
|
|
303
|
+
else:
|
|
304
|
+
server_info = self.registry_client.find_server_by_reference(server_url)
|
|
305
|
+
|
|
306
|
+
if not server_info:
|
|
307
|
+
print(f"Error: MCP server '{server_url}' not found in registry")
|
|
308
|
+
return False
|
|
309
|
+
|
|
310
|
+
# Determine config key
|
|
311
|
+
if server_name:
|
|
312
|
+
config_key = server_name
|
|
313
|
+
elif "/" in server_url:
|
|
314
|
+
config_key = server_url.split("/")[-1]
|
|
315
|
+
else:
|
|
316
|
+
config_key = server_url
|
|
317
|
+
|
|
318
|
+
server_config = self._format_server_config(server_info, env_overrides, runtime_vars)
|
|
319
|
+
self.update_config({config_key: server_config})
|
|
320
|
+
|
|
321
|
+
print(f"Successfully configured MCP server '{config_key}' for Cursor")
|
|
322
|
+
return True
|
|
323
|
+
|
|
324
|
+
except Exception as e:
|
|
325
|
+
print(f"Error configuring MCP server: {e}")
|
|
326
|
+
return False
|
|
@@ -152,10 +152,21 @@ def _looks_like_legacy_apm_bundle(path: Path) -> bool:
|
|
|
152
152
|
for member in tar.getmembers():
|
|
153
153
|
if member.issym() or member.islnk():
|
|
154
154
|
return False
|
|
155
|
+
name = member.name
|
|
156
|
+
if (
|
|
157
|
+
name.startswith("/")
|
|
158
|
+
or PureWindowsPath(name).drive
|
|
159
|
+
or PureWindowsPath(name).is_absolute()
|
|
160
|
+
):
|
|
161
|
+
return False
|
|
162
|
+
try:
|
|
163
|
+
validate_path_segments(name, context="tar member")
|
|
164
|
+
except PathTraversalError:
|
|
165
|
+
return False
|
|
155
166
|
if sys.version_info >= (3, 12):
|
|
156
167
|
tar.extractall(tmp, filter="data")
|
|
157
168
|
else:
|
|
158
|
-
tar.extractall(tmp) # noqa: S202
|
|
169
|
+
tar.extractall(tmp) # noqa: S202 -- validated above
|
|
159
170
|
# Locate the inner directory (apm pack uses arcname=<bundle-name>)
|
|
160
171
|
root = tmp
|
|
161
172
|
children = [p for p in tmp.iterdir() if p.is_dir()]
|
|
@@ -365,6 +376,7 @@ def check_target_mismatch(
|
|
|
365
376
|
Returns ``None`` when:
|
|
366
377
|
|
|
367
378
|
- ``bundle_targets`` is empty (pre-constraint bundle, no metadata), OR
|
|
379
|
+
- ``bundle_targets`` contains ``"all"`` (target-agnostic bundle), OR
|
|
368
380
|
- ``install_targets`` is a superset of ``bundle_targets``.
|
|
369
381
|
|
|
370
382
|
Otherwise returns a human-readable warning naming the missing targets.
|
|
@@ -372,6 +384,11 @@ def check_target_mismatch(
|
|
|
372
384
|
if not bundle_targets:
|
|
373
385
|
return None
|
|
374
386
|
bundle_set = {t.strip() for t in bundle_targets if t and t.strip()}
|
|
387
|
+
# Issue #1207: ``"all"`` (or empty after stripping) means target-agnostic.
|
|
388
|
+
# Such bundles cover any install target, so no mismatch warning is
|
|
389
|
+
# appropriate.
|
|
390
|
+
if "all" in bundle_set:
|
|
391
|
+
return None
|
|
375
392
|
install_set = {t.strip() for t in install_targets if t and t.strip()}
|
|
376
393
|
missing = sorted(bundle_set - install_set)
|
|
377
394
|
if not missing:
|
|
@@ -636,10 +636,17 @@ def export_plugin_bundle(
|
|
|
636
636
|
if rel == "apm.lock.yaml":
|
|
637
637
|
continue
|
|
638
638
|
bundle_files[rel] = hashlib.sha256(fp.read_bytes()).hexdigest()
|
|
639
|
+
# Issue #1207 D1: do NOT silently substitute ``"copilot"`` when
|
|
640
|
+
# ``target`` is missing. Bundles are target-agnostic at install
|
|
641
|
+
# time; ``pack.target`` is recorded as informational metadata only.
|
|
642
|
+
# Falling back to ``"all"`` preserves the lockfile-filter shape
|
|
643
|
+
# (which uses target prefixes to narrow each dep's deployed_files
|
|
644
|
+
# list to the union of supported targets) without locking the
|
|
645
|
+
# bundle to a single client.
|
|
639
646
|
enriched_yaml = enrich_lockfile_for_pack(
|
|
640
647
|
lockfile,
|
|
641
648
|
"plugin",
|
|
642
|
-
target or "
|
|
649
|
+
target or "all",
|
|
643
650
|
bundle_files=bundle_files,
|
|
644
651
|
)
|
|
645
652
|
(bundle_dir / "apm.lock.yaml").write_text(enriched_yaml, encoding="utf-8")
|
|
@@ -5,10 +5,11 @@ import sys
|
|
|
5
5
|
import tarfile
|
|
6
6
|
import tempfile
|
|
7
7
|
from dataclasses import dataclass, field
|
|
8
|
-
from pathlib import Path
|
|
8
|
+
from pathlib import Path, PureWindowsPath
|
|
9
9
|
from typing import Dict, List # noqa: F401, UP035
|
|
10
10
|
|
|
11
11
|
from ..deps.lockfile import LEGACY_LOCKFILE_NAME, LOCKFILE_NAME, LockFile
|
|
12
|
+
from ..utils.path_security import PathTraversalError, validate_path_segments
|
|
12
13
|
|
|
13
14
|
|
|
14
15
|
@dataclass
|
|
@@ -64,10 +65,21 @@ def unpack_bundle(
|
|
|
64
65
|
with tarfile.open(bundle_path, "r:gz") as tar:
|
|
65
66
|
# Security: prevent path traversal and special entries
|
|
66
67
|
for member in tar.getmembers():
|
|
67
|
-
|
|
68
|
-
|
|
68
|
+
name = member.name
|
|
69
|
+
if (
|
|
70
|
+
name.startswith("/")
|
|
71
|
+
or PureWindowsPath(name).drive
|
|
72
|
+
or PureWindowsPath(name).is_absolute()
|
|
73
|
+
):
|
|
74
|
+
raise ValueError(f"Refusing to extract path-traversal entry: {name}")
|
|
75
|
+
try:
|
|
76
|
+
validate_path_segments(name, context="tar member")
|
|
77
|
+
except PathTraversalError:
|
|
78
|
+
raise ValueError(
|
|
79
|
+
f"Refusing to extract path-traversal entry: {name}"
|
|
80
|
+
) from None
|
|
69
81
|
if member.issym() or member.islnk():
|
|
70
|
-
raise ValueError(f"Refusing to extract symlink/hardlink: {
|
|
82
|
+
raise ValueError(f"Refusing to extract symlink/hardlink: {name}")
|
|
71
83
|
# filter="data" was added in Python 3.12; use it when available
|
|
72
84
|
if sys.version_info >= (3, 12):
|
|
73
85
|
tar.extractall(temp_dir, filter="data")
|
|
@@ -158,6 +158,9 @@ class GitCache:
|
|
|
158
158
|
from ..utils.git_env import get_git_executable, git_subprocess_env
|
|
159
159
|
|
|
160
160
|
git_exe = get_git_executable()
|
|
161
|
+
# auth-delegated: cache-layer ref resolution runs after lockfile
|
|
162
|
+
# already pinned the commit; no PAT->bearer fallback applies here
|
|
163
|
+
# (env is sanitized, no embedded creds).
|
|
161
164
|
cmd = [git_exe, "ls-remote", url]
|
|
162
165
|
if ref:
|
|
163
166
|
cmd.append(ref)
|
|
@@ -35,6 +35,7 @@ from apm_cli.commands.policy import policy
|
|
|
35
35
|
from apm_cli.commands.prune import prune
|
|
36
36
|
from apm_cli.commands.run import preview, run
|
|
37
37
|
from apm_cli.commands.runtime import runtime
|
|
38
|
+
from apm_cli.commands.self_update import self_update
|
|
38
39
|
from apm_cli.commands.targets import targets
|
|
39
40
|
from apm_cli.commands.uninstall import uninstall
|
|
40
41
|
from apm_cli.commands.update import update
|
|
@@ -90,6 +91,7 @@ cli.add_command(install)
|
|
|
90
91
|
cli.add_command(uninstall)
|
|
91
92
|
cli.add_command(prune)
|
|
92
93
|
cli.add_command(update)
|
|
94
|
+
cli.add_command(self_update)
|
|
93
95
|
cli.add_command(compile_cmd, name="compile")
|
|
94
96
|
cli.add_command(run)
|
|
95
97
|
cli.add_command(preview)
|
|
@@ -25,7 +25,18 @@ def set_skill_subset_for_entry(
|
|
|
25
25
|
Returns True if file was modified.
|
|
26
26
|
"""
|
|
27
27
|
data = load_yaml(manifest_path) or {}
|
|
28
|
-
deps_section = data.get("dependencies"
|
|
28
|
+
deps_section = data.get("dependencies")
|
|
29
|
+
if deps_section is None:
|
|
30
|
+
deps_section = {}
|
|
31
|
+
if not isinstance(deps_section, dict):
|
|
32
|
+
raise ValueError(
|
|
33
|
+
f"Invalid 'dependencies' in {manifest_path}: expected a mapping "
|
|
34
|
+
f"with 'apm:' key, got {type(deps_section).__name__}. "
|
|
35
|
+
"Use the structured format:\n"
|
|
36
|
+
" dependencies:\n"
|
|
37
|
+
" apm:\n"
|
|
38
|
+
" - owner/repo"
|
|
39
|
+
)
|
|
29
40
|
apm_deps = deps_section.get("apm", [])
|
|
30
41
|
if not apm_deps:
|
|
31
42
|
return False
|
|
@@ -53,7 +53,12 @@ def _get_config_getters():
|
|
|
53
53
|
|
|
54
54
|
def _valid_config_keys() -> str:
|
|
55
55
|
"""Return valid config keys for messages."""
|
|
56
|
-
|
|
56
|
+
from ..core.experimental import is_enabled
|
|
57
|
+
|
|
58
|
+
keys = ["auto-integrate", "temp-dir"]
|
|
59
|
+
if is_enabled("copilot_cowork"):
|
|
60
|
+
keys.append("copilot-cowork-skills-dir")
|
|
61
|
+
return ", ".join(keys)
|
|
57
62
|
|
|
58
63
|
|
|
59
64
|
@click.group(help="Configure APM CLI", invoke_without_command=True)
|