apm-cli 0.7.8__tar.gz → 0.7.9__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.7.8/src/apm_cli.egg-info → apm_cli-0.7.9}/PKG-INFO +24 -2
- {apm_cli-0.7.8 → apm_cli-0.7.9}/README.md +22 -0
- {apm_cli-0.7.8 → apm_cli-0.7.9}/pyproject.toml +2 -2
- {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/adapters/client/codex.py +3 -3
- {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/adapters/client/copilot.py +2 -2
- {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/adapters/client/vscode.py +1 -1
- {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/bundle/lockfile_enrichment.py +1 -1
- {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/bundle/packer.py +26 -13
- {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/bundle/unpacker.py +15 -10
- {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/cli.py +46 -1
- {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/commands/_helpers.py +4 -4
- {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/commands/compile.py +42 -42
- {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/commands/deps.py +38 -38
- {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/commands/init.py +5 -5
- {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/commands/install.py +339 -11
- {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/commands/list_cmd.py +2 -2
- {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/commands/mcp.py +19 -19
- {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/commands/pack.py +4 -4
- {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/commands/prune.py +7 -8
- {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/commands/run.py +6 -6
- {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/commands/runtime.py +5 -5
- {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/commands/uninstall.py +18 -18
- {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/commands/update.py +50 -19
- {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/compilation/agents_compiler.py +7 -7
- {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/compilation/context_optimizer.py +14 -8
- {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/compilation/distributed_compiler.py +7 -7
- {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/compilation/link_resolver.py +5 -4
- {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/core/safe_installer.py +6 -6
- {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/core/script_runner.py +29 -15
- {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/core/target_detection.py +4 -4
- {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/core/token_manager.py +1 -1
- {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/deps/apm_resolver.py +1 -1
- {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/deps/github_downloader.py +18 -10
- {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/deps/lockfile.py +59 -5
- {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/deps/plugin_parser.py +22 -22
- {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/integration/agent_integrator.py +1 -1
- {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/integration/base_integrator.py +6 -6
- {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/integration/hook_integrator.py +14 -12
- {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/integration/mcp_integrator.py +35 -35
- {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/integration/prompt_integrator.py +1 -1
- {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/integration/skill_integrator.py +95 -29
- {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/integration/skill_transformer.py +2 -2
- {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/models/dependency.py +105 -23
- {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/models/validation.py +5 -5
- {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/output/formatters.py +85 -85
- {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/output/script_formatters.py +21 -21
- {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/primitives/models.py +2 -2
- {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/registry/operations.py +2 -2
- {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/runtime/manager.py +78 -44
- {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/utils/console.py +25 -25
- {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/utils/diagnostics.py +5 -4
- {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/utils/github_host.py +4 -4
- {apm_cli-0.7.8 → apm_cli-0.7.9/src/apm_cli.egg-info}/PKG-INFO +24 -2
- {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli.egg-info/requires.txt +3 -1
- {apm_cli-0.7.8 → apm_cli-0.7.9}/tests/test_apm_package_models.py +9 -4
- {apm_cli-0.7.8 → apm_cli-0.7.9}/tests/test_apm_resolver.py +1 -1
- {apm_cli-0.7.8 → apm_cli-0.7.9}/tests/test_enhanced_discovery.py +3 -3
- {apm_cli-0.7.8 → apm_cli-0.7.9}/tests/test_github_downloader.py +23 -2
- {apm_cli-0.7.8 → apm_cli-0.7.9}/tests/test_lockfile.py +42 -3
- {apm_cli-0.7.8 → apm_cli-0.7.9}/tests/test_runnable_prompts.py +2 -2
- {apm_cli-0.7.8 → apm_cli-0.7.9}/AUTHORS +0 -0
- {apm_cli-0.7.8 → apm_cli-0.7.9}/LICENSE +0 -0
- {apm_cli-0.7.8 → apm_cli-0.7.9}/setup.cfg +0 -0
- {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/__init__.py +0 -0
- {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/adapters/__init__.py +0 -0
- {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/adapters/client/__init__.py +0 -0
- {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/adapters/client/base.py +0 -0
- {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/adapters/package_manager/__init__.py +0 -0
- {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/adapters/package_manager/base.py +0 -0
- {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/adapters/package_manager/default_manager.py +0 -0
- {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/bundle/__init__.py +0 -0
- {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/commands/__init__.py +0 -0
- {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/commands/config.py +0 -0
- {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/compilation/__init__.py +0 -0
- {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/compilation/claude_formatter.py +0 -0
- {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/compilation/constants.py +0 -0
- {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/compilation/constitution.py +0 -0
- {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/compilation/constitution_block.py +0 -0
- {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/compilation/injector.py +0 -0
- {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/compilation/template_builder.py +0 -0
- {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/config.py +0 -0
- {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/core/__init__.py +0 -0
- {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/core/conflict_detector.py +0 -0
- {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/core/docker_args.py +0 -0
- {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/core/operations.py +0 -0
- {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/deps/__init__.py +0 -0
- {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/deps/aggregator.py +0 -0
- {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/deps/collection_parser.py +0 -0
- {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/deps/dependency_graph.py +0 -0
- {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/deps/package_validator.py +0 -0
- {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/deps/verifier.py +0 -0
- {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/drift.py +0 -0
- {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/factory.py +0 -0
- {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/integration/__init__.py +0 -0
- {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/integration/command_integrator.py +0 -0
- {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/integration/instruction_integrator.py +0 -0
- {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/integration/utils.py +0 -0
- {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/models/__init__.py +0 -0
- {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/models/apm_package.py +0 -0
- {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/models/plugin.py +0 -0
- {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/output/__init__.py +0 -0
- {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/output/models.py +0 -0
- {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/primitives/__init__.py +0 -0
- {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/primitives/discovery.py +0 -0
- {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/primitives/parser.py +0 -0
- {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/registry/__init__.py +0 -0
- {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/registry/client.py +0 -0
- {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/registry/integration.py +0 -0
- {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/runtime/__init__.py +0 -0
- {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/runtime/base.py +0 -0
- {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/runtime/codex_runtime.py +0 -0
- {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/runtime/copilot_runtime.py +0 -0
- {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/runtime/factory.py +0 -0
- {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/runtime/llm_runtime.py +0 -0
- {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/utils/__init__.py +0 -0
- {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/utils/helpers.py +0 -0
- {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/utils/version_checker.py +0 -0
- {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/version.py +0 -0
- {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/workflow/__init__.py +0 -0
- {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/workflow/discovery.py +0 -0
- {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/workflow/parser.py +0 -0
- {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli/workflow/runner.py +0 -0
- {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli.egg-info/SOURCES.txt +0 -0
- {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli.egg-info/dependency_links.txt +0 -0
- {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli.egg-info/entry_points.txt +0 -0
- {apm_cli-0.7.8 → apm_cli-0.7.9}/src/apm_cli.egg-info/top_level.txt +0 -0
- {apm_cli-0.7.8 → apm_cli-0.7.9}/tests/test_codex_docker_args_fix.py +0 -0
- {apm_cli-0.7.8 → apm_cli-0.7.9}/tests/test_codex_empty_string_and_defaults.py +0 -0
- {apm_cli-0.7.8 → apm_cli-0.7.9}/tests/test_collision_integration.py +0 -0
- {apm_cli-0.7.8 → apm_cli-0.7.9}/tests/test_console.py +0 -0
- {apm_cli-0.7.8 → apm_cli-0.7.9}/tests/test_distributed_compilation.py +0 -0
- {apm_cli-0.7.8 → apm_cli-0.7.9}/tests/test_empty_string_and_defaults.py +0 -0
- {apm_cli-0.7.8 → apm_cli-0.7.9}/tests/test_github_downloader_token_precedence.py +0 -0
- {apm_cli-0.7.8 → apm_cli-0.7.9}/tests/test_runtime_manager_token_precedence.py +0 -0
- {apm_cli-0.7.8 → apm_cli-0.7.9}/tests/test_virtual_package_multi_install.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: apm-cli
|
|
3
|
-
Version: 0.7.
|
|
3
|
+
Version: 0.7.9
|
|
4
4
|
Summary: MCP configuration tool
|
|
5
5
|
Author-email: Daniel Meppiel <user@example.com>
|
|
6
6
|
License: MIT License
|
|
@@ -52,7 +52,7 @@ Requires-Dist: GitPython>=3.1.0
|
|
|
52
52
|
Provides-Extra: dev
|
|
53
53
|
Requires-Dist: pytest>=7.0.0; extra == "dev"
|
|
54
54
|
Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
|
|
55
|
-
Requires-Dist: black>=
|
|
55
|
+
Requires-Dist: black>=26.3.1; python_version >= "3.10" and extra == "dev"
|
|
56
56
|
Requires-Dist: isort>=5.0.0; extra == "dev"
|
|
57
57
|
Requires-Dist: mypy>=1.0.0; extra == "dev"
|
|
58
58
|
Provides-Extra: build
|
|
@@ -107,13 +107,25 @@ apm install # every agent is configured
|
|
|
107
107
|
|
|
108
108
|
## Get Started
|
|
109
109
|
|
|
110
|
+
#### Linux / macOS
|
|
111
|
+
|
|
110
112
|
```bash
|
|
111
113
|
curl -sSL https://raw.githubusercontent.com/microsoft/apm/main/install.sh | sh
|
|
112
114
|
```
|
|
113
115
|
|
|
116
|
+
#### Windows
|
|
117
|
+
|
|
118
|
+
```powershell
|
|
119
|
+
irm https://raw.githubusercontent.com/microsoft/apm/main/install.ps1 | iex
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
Native release binaries are published for macOS, Linux, and Windows x86_64. `apm update` reuses the matching platform installer.
|
|
123
|
+
|
|
114
124
|
<details>
|
|
115
125
|
<summary>Other install methods</summary>
|
|
116
126
|
|
|
127
|
+
#### Linux / macOS
|
|
128
|
+
|
|
117
129
|
```bash
|
|
118
130
|
# Homebrew
|
|
119
131
|
brew install microsoft/apm/apm
|
|
@@ -121,6 +133,16 @@ brew install microsoft/apm/apm
|
|
|
121
133
|
pip install apm-cli
|
|
122
134
|
```
|
|
123
135
|
|
|
136
|
+
#### Windows
|
|
137
|
+
|
|
138
|
+
```powershell
|
|
139
|
+
# Scoop
|
|
140
|
+
scoop bucket add apm https://github.com/microsoft/scoop-apm
|
|
141
|
+
scoop install apm
|
|
142
|
+
# pip
|
|
143
|
+
pip install apm-cli
|
|
144
|
+
```
|
|
145
|
+
|
|
124
146
|
</details>
|
|
125
147
|
|
|
126
148
|
Then start adding packages:
|
|
@@ -46,13 +46,25 @@ apm install # every agent is configured
|
|
|
46
46
|
|
|
47
47
|
## Get Started
|
|
48
48
|
|
|
49
|
+
#### Linux / macOS
|
|
50
|
+
|
|
49
51
|
```bash
|
|
50
52
|
curl -sSL https://raw.githubusercontent.com/microsoft/apm/main/install.sh | sh
|
|
51
53
|
```
|
|
52
54
|
|
|
55
|
+
#### Windows
|
|
56
|
+
|
|
57
|
+
```powershell
|
|
58
|
+
irm https://raw.githubusercontent.com/microsoft/apm/main/install.ps1 | iex
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Native release binaries are published for macOS, Linux, and Windows x86_64. `apm update` reuses the matching platform installer.
|
|
62
|
+
|
|
53
63
|
<details>
|
|
54
64
|
<summary>Other install methods</summary>
|
|
55
65
|
|
|
66
|
+
#### Linux / macOS
|
|
67
|
+
|
|
56
68
|
```bash
|
|
57
69
|
# Homebrew
|
|
58
70
|
brew install microsoft/apm/apm
|
|
@@ -60,6 +72,16 @@ brew install microsoft/apm/apm
|
|
|
60
72
|
pip install apm-cli
|
|
61
73
|
```
|
|
62
74
|
|
|
75
|
+
#### Windows
|
|
76
|
+
|
|
77
|
+
```powershell
|
|
78
|
+
# Scoop
|
|
79
|
+
scoop bucket add apm https://github.com/microsoft/scoop-apm
|
|
80
|
+
scoop install apm
|
|
81
|
+
# pip
|
|
82
|
+
pip install apm-cli
|
|
83
|
+
```
|
|
84
|
+
|
|
63
85
|
</details>
|
|
64
86
|
|
|
65
87
|
Then start adding packages:
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "apm-cli"
|
|
7
|
-
version = "0.7.
|
|
7
|
+
version = "0.7.9"
|
|
8
8
|
description = "MCP configuration tool"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
@@ -41,7 +41,7 @@ dependencies = [
|
|
|
41
41
|
dev = [
|
|
42
42
|
"pytest>=7.0.0",
|
|
43
43
|
"pytest-cov>=4.0.0",
|
|
44
|
-
"black>=
|
|
44
|
+
"black>=26.3.1; python_version>='3.10'",
|
|
45
45
|
"isort>=5.0.0",
|
|
46
46
|
"mypy>=1.0.0",
|
|
47
47
|
]
|
|
@@ -122,7 +122,7 @@ class CodexClientAdapter(MCPClientAdapter):
|
|
|
122
122
|
|
|
123
123
|
# If server has only remote endpoints and no packages, it's a remote-only server
|
|
124
124
|
if remotes and not packages:
|
|
125
|
-
print(f"
|
|
125
|
+
print(f"[!] Warning: MCP server '{server_url}' is a remote server (SSE type)")
|
|
126
126
|
print(" Codex CLI only supports local servers with command/args configuration")
|
|
127
127
|
print(" Remote servers are not supported by Codex CLI")
|
|
128
128
|
print(" Skipping installation for Codex CLI")
|
|
@@ -174,7 +174,7 @@ class CodexClientAdapter(MCPClientAdapter):
|
|
|
174
174
|
"id": server_info.get("id", "") # Add registry UUID for conflict detection
|
|
175
175
|
}
|
|
176
176
|
|
|
177
|
-
# Self-defined stdio deps carry raw command/args
|
|
177
|
+
# Self-defined stdio deps carry raw command/args -- use directly
|
|
178
178
|
raw = server_info.get("_raw_stdio")
|
|
179
179
|
if raw:
|
|
180
180
|
config["command"] = raw["command"]
|
|
@@ -328,7 +328,7 @@ class CodexClientAdapter(MCPClientAdapter):
|
|
|
328
328
|
# Check for CI/automated environment via APM_E2E_TESTS flag (more reliable than TTY detection)
|
|
329
329
|
if os.getenv('APM_E2E_TESTS') == '1':
|
|
330
330
|
skip_prompting = True
|
|
331
|
-
print(f"
|
|
331
|
+
print(f" APM_E2E_TESTS detected, will skip environment variable prompts")
|
|
332
332
|
|
|
333
333
|
# Also skip prompting if we're in a non-interactive environment (fallback)
|
|
334
334
|
is_interactive = sys.stdin.isatty() and sys.stdout.isatty()
|
|
@@ -166,7 +166,7 @@ class CopilotClientAdapter(MCPClientAdapter):
|
|
|
166
166
|
"id": server_info.get("id", "") # Add registry UUID for conflict detection
|
|
167
167
|
}
|
|
168
168
|
|
|
169
|
-
# Self-defined stdio deps carry raw command/args
|
|
169
|
+
# Self-defined stdio deps carry raw command/args -- use directly
|
|
170
170
|
raw = server_info.get("_raw_stdio")
|
|
171
171
|
if raw:
|
|
172
172
|
config["command"] = raw["command"]
|
|
@@ -331,7 +331,7 @@ class CopilotClientAdapter(MCPClientAdapter):
|
|
|
331
331
|
# Check for CI/automated environment via APM_E2E_TESTS flag (more reliable than TTY detection)
|
|
332
332
|
if os.getenv('APM_E2E_TESTS') == '1':
|
|
333
333
|
skip_prompting = True
|
|
334
|
-
print(f"
|
|
334
|
+
print(f" APM_E2E_TESTS detected, will skip environment variable prompts")
|
|
335
335
|
|
|
336
336
|
# Also skip prompting if we're in a non-interactive environment (fallback)
|
|
337
337
|
is_interactive = sys.stdin.isatty() and sys.stdout.isatty()
|
|
@@ -187,7 +187,7 @@ class VSCodeClientAdapter(MCPClientAdapter):
|
|
|
187
187
|
server_config = {}
|
|
188
188
|
input_vars = []
|
|
189
189
|
|
|
190
|
-
# Self-defined stdio deps carry raw command/args
|
|
190
|
+
# Self-defined stdio deps carry raw command/args -- use directly
|
|
191
191
|
raw = server_info.get("_raw_stdio")
|
|
192
192
|
if raw:
|
|
193
193
|
server_config = {
|
|
@@ -12,7 +12,7 @@ def enrich_lockfile_for_pack(
|
|
|
12
12
|
) -> str:
|
|
13
13
|
"""Create an enriched copy of the lockfile YAML with a ``pack:`` section.
|
|
14
14
|
|
|
15
|
-
Does NOT mutate the original *lockfile* object
|
|
15
|
+
Does NOT mutate the original *lockfile* object -- serialises a copy and
|
|
16
16
|
prepends the pack metadata.
|
|
17
17
|
|
|
18
18
|
Args:
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""Bundle packer
|
|
1
|
+
"""Bundle packer -- creates self-contained APM bundles from the resolved dependency tree."""
|
|
2
2
|
|
|
3
3
|
import shutil
|
|
4
4
|
import tarfile
|
|
@@ -6,7 +6,7 @@ from dataclasses import dataclass, field
|
|
|
6
6
|
from pathlib import Path
|
|
7
7
|
from typing import List, Optional
|
|
8
8
|
|
|
9
|
-
from ..deps.lockfile import LockFile
|
|
9
|
+
from ..deps.lockfile import LockFile, get_lockfile_path, migrate_lockfile_if_needed
|
|
10
10
|
from ..models.apm_package import APMPackage
|
|
11
11
|
from ..core.target_detection import detect_target
|
|
12
12
|
from .lockfile_enrichment import enrich_lockfile_for_pack
|
|
@@ -47,10 +47,10 @@ def pack_bundle(
|
|
|
47
47
|
"""Create a self-contained bundle from installed APM dependencies.
|
|
48
48
|
|
|
49
49
|
Args:
|
|
50
|
-
project_root: Root of the project containing ``apm.lock`` and ``apm.yml``.
|
|
50
|
+
project_root: Root of the project containing ``apm.lock.yaml`` and ``apm.yml``.
|
|
51
51
|
output_dir: Directory where the bundle will be created.
|
|
52
|
-
fmt: Bundle format
|
|
53
|
-
target: Target filter
|
|
52
|
+
fmt: Bundle format -- ``"apm"`` (default) or ``"plugin"``.
|
|
53
|
+
target: Target filter -- ``"vscode"``, ``"claude"``, ``"all"``, or *None*
|
|
54
54
|
(auto-detect from apm.yml / project structure).
|
|
55
55
|
archive: If *True*, produce a ``.tar.gz`` and remove the directory.
|
|
56
56
|
dry_run: If *True*, resolve the file list but write nothing to disk.
|
|
@@ -59,15 +59,16 @@ def pack_bundle(
|
|
|
59
59
|
:class:`PackResult` describing what was (or would be) produced.
|
|
60
60
|
|
|
61
61
|
Raises:
|
|
62
|
-
FileNotFoundError: If ``apm.lock`` is missing.
|
|
62
|
+
FileNotFoundError: If ``apm.lock.yaml`` is missing.
|
|
63
63
|
ValueError: If deployed files referenced in the lockfile are missing on disk.
|
|
64
64
|
"""
|
|
65
|
-
# 1. Read lockfile
|
|
66
|
-
|
|
65
|
+
# 1. Read lockfile (migrate legacy apm.lock → apm.lock.yaml if needed)
|
|
66
|
+
migrate_lockfile_if_needed(project_root)
|
|
67
|
+
lockfile_path = get_lockfile_path(project_root)
|
|
67
68
|
lockfile = LockFile.read(lockfile_path)
|
|
68
69
|
if lockfile is None:
|
|
69
70
|
raise FileNotFoundError(
|
|
70
|
-
"apm.lock not found
|
|
71
|
+
"apm.lock.yaml not found -- run 'apm install' first to resolve dependencies."
|
|
71
72
|
)
|
|
72
73
|
|
|
73
74
|
# 2. Read apm.yml for name / version / config target
|
|
@@ -77,7 +78,19 @@ def pack_bundle(
|
|
|
77
78
|
pkg_name = package.name
|
|
78
79
|
pkg_version = package.version or "0.0.0"
|
|
79
80
|
config_target = package.target
|
|
80
|
-
|
|
81
|
+
|
|
82
|
+
# Guard: reject local-path dependencies (non-portable)
|
|
83
|
+
for dep_ref in package.get_apm_dependencies():
|
|
84
|
+
if dep_ref.is_local:
|
|
85
|
+
raise ValueError(
|
|
86
|
+
f"Cannot pack — apm.yml contains local path dependency: "
|
|
87
|
+
f"{dep_ref.local_path}\n"
|
|
88
|
+
f"Local dependencies are for development only. Replace them with "
|
|
89
|
+
f"remote references (e.g., 'owner/repo') before packing."
|
|
90
|
+
)
|
|
91
|
+
except ValueError:
|
|
92
|
+
raise
|
|
93
|
+
except FileNotFoundError:
|
|
81
94
|
pkg_name = project_root.resolve().name
|
|
82
95
|
pkg_version = "0.0.0"
|
|
83
96
|
config_target = None
|
|
@@ -88,7 +101,7 @@ def pack_bundle(
|
|
|
88
101
|
explicit_target=target,
|
|
89
102
|
config_target=config_target,
|
|
90
103
|
)
|
|
91
|
-
# For packing purposes, "minimal" means nothing to pack
|
|
104
|
+
# For packing purposes, "minimal" means nothing to pack -- treat as "all"
|
|
92
105
|
if effective_target == "minimal":
|
|
93
106
|
effective_target = "all"
|
|
94
107
|
|
|
@@ -126,7 +139,7 @@ def pack_bundle(
|
|
|
126
139
|
missing.append(rel_path)
|
|
127
140
|
if missing:
|
|
128
141
|
raise ValueError(
|
|
129
|
-
f"The following deployed files are missing on disk
|
|
142
|
+
f"The following deployed files are missing on disk -- "
|
|
130
143
|
f"run 'apm install' to restore them:\n"
|
|
131
144
|
+ "\n".join(f" - {m}" for m in missing)
|
|
132
145
|
)
|
|
@@ -156,7 +169,7 @@ def pack_bundle(
|
|
|
156
169
|
|
|
157
170
|
# 8. Enrich lockfile copy and write to bundle
|
|
158
171
|
enriched_yaml = enrich_lockfile_for_pack(lockfile, fmt, effective_target)
|
|
159
|
-
(bundle_dir / "apm.lock").write_text(enriched_yaml, encoding="utf-8")
|
|
172
|
+
(bundle_dir / "apm.lock.yaml").write_text(enriched_yaml, encoding="utf-8")
|
|
160
173
|
|
|
161
174
|
result = PackResult(
|
|
162
175
|
bundle_path=bundle_dir,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""Bundle unpacker
|
|
1
|
+
"""Bundle unpacker -- extracts and verifies APM bundles."""
|
|
2
2
|
|
|
3
3
|
import shutil
|
|
4
4
|
import sys
|
|
@@ -8,7 +8,7 @@ from dataclasses import dataclass, field
|
|
|
8
8
|
from pathlib import Path
|
|
9
9
|
from typing import Dict, List
|
|
10
10
|
|
|
11
|
-
from ..deps.lockfile import LockFile
|
|
11
|
+
from ..deps.lockfile import LockFile, LOCKFILE_NAME, LEGACY_LOCKFILE_NAME
|
|
12
12
|
|
|
13
13
|
|
|
14
14
|
@dataclass
|
|
@@ -44,7 +44,7 @@ def unpack_bundle(
|
|
|
44
44
|
:class:`UnpackResult` describing what was (or would be) extracted.
|
|
45
45
|
|
|
46
46
|
Raises:
|
|
47
|
-
FileNotFoundError: If the bundle's ``apm.lock`` is missing.
|
|
47
|
+
FileNotFoundError: If the bundle's ``apm.lock.yaml`` is missing.
|
|
48
48
|
ValueError: If verification finds files listed in the lockfile but
|
|
49
49
|
absent from the bundle.
|
|
50
50
|
"""
|
|
@@ -65,7 +65,7 @@ def unpack_bundle(
|
|
|
65
65
|
if sys.version_info >= (3, 12):
|
|
66
66
|
tar.extractall(temp_dir, filter="data")
|
|
67
67
|
else:
|
|
68
|
-
tar.extractall(temp_dir) # noqa: S202
|
|
68
|
+
tar.extractall(temp_dir) # noqa: S202 -- manual checks above
|
|
69
69
|
except Exception:
|
|
70
70
|
shutil.rmtree(temp_dir, ignore_errors=True)
|
|
71
71
|
raise
|
|
@@ -83,16 +83,21 @@ def unpack_bundle(
|
|
|
83
83
|
raise FileNotFoundError(f"Bundle not found or unsupported format: {bundle_path}")
|
|
84
84
|
|
|
85
85
|
try:
|
|
86
|
-
# 2. Read apm.lock from bundle
|
|
87
|
-
lockfile_path = source_dir /
|
|
86
|
+
# 2. Read apm.lock.yaml (or legacy apm.lock) from bundle
|
|
87
|
+
lockfile_path = source_dir / LOCKFILE_NAME
|
|
88
|
+
if not lockfile_path.exists():
|
|
89
|
+
# Backward compat: older bundles used "apm.lock"
|
|
90
|
+
legacy_lockfile_path = source_dir / LEGACY_LOCKFILE_NAME
|
|
91
|
+
if legacy_lockfile_path.exists():
|
|
92
|
+
lockfile_path = legacy_lockfile_path
|
|
88
93
|
lockfile = LockFile.read(lockfile_path)
|
|
89
94
|
if lockfile is None:
|
|
90
95
|
if not lockfile_path.exists():
|
|
91
96
|
raise FileNotFoundError(
|
|
92
|
-
"
|
|
97
|
+
f"{lockfile_path.name} not found in the bundle -- the bundle may be incomplete."
|
|
93
98
|
)
|
|
94
99
|
raise FileNotFoundError(
|
|
95
|
-
"
|
|
100
|
+
f"{lockfile_path.name} in the bundle could not be parsed -- the bundle may be corrupt."
|
|
96
101
|
)
|
|
97
102
|
|
|
98
103
|
# Collect deployed_files per dependency and deduplicated global list
|
|
@@ -118,7 +123,7 @@ def unpack_bundle(
|
|
|
118
123
|
]
|
|
119
124
|
if missing:
|
|
120
125
|
raise ValueError(
|
|
121
|
-
"Bundle verification failed
|
|
126
|
+
"Bundle verification failed -- the following deployed files "
|
|
122
127
|
"are missing from the bundle:\n"
|
|
123
128
|
+ "\n".join(f" - {m}" for m in missing)
|
|
124
129
|
)
|
|
@@ -142,7 +147,7 @@ def unpack_bundle(
|
|
|
142
147
|
for rel_path in unique_files:
|
|
143
148
|
# Guard against absolute paths or path-traversal entries in deployed_files
|
|
144
149
|
p = Path(rel_path)
|
|
145
|
-
if p.is_absolute() or ".." in p.parts:
|
|
150
|
+
if p.is_absolute() or rel_path.startswith("/") or ".." in p.parts:
|
|
146
151
|
raise ValueError(
|
|
147
152
|
f"Refusing to unpack unsafe path from bundle lockfile: {rel_path!r}"
|
|
148
153
|
)
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
"""Command-line interface for Agent Package Manager (APM).
|
|
2
2
|
|
|
3
|
-
Thin wiring layer
|
|
3
|
+
Thin wiring layer -- all command logic lives in ``apm_cli.commands.*`` modules.
|
|
4
4
|
"""
|
|
5
5
|
|
|
6
|
+
import ctypes
|
|
7
|
+
import os
|
|
6
8
|
import sys
|
|
7
9
|
|
|
8
10
|
import click
|
|
@@ -67,8 +69,51 @@ cli.add_command(runtime)
|
|
|
67
69
|
cli.add_command(mcp)
|
|
68
70
|
|
|
69
71
|
|
|
72
|
+
def _configure_encoding() -> None:
|
|
73
|
+
"""Configure stdout/stderr for full Unicode on Windows.
|
|
74
|
+
|
|
75
|
+
The default Windows console encoding (cp1252) cannot represent many Unicode
|
|
76
|
+
characters used in APM output (box-drawing, check marks, arrows, etc.).
|
|
77
|
+
|
|
78
|
+
This function:
|
|
79
|
+
1. Sets ``PYTHONIOENCODING`` so child processes and redirected pipes
|
|
80
|
+
default to UTF-8.
|
|
81
|
+
2. Switches the console codepage to 65001 (UTF-8) via the Win32 API so
|
|
82
|
+
the terminal itself renders UTF-8 byte sequences correctly.
|
|
83
|
+
3. Reconfigures the Python text-mode streams to UTF-8.
|
|
84
|
+
|
|
85
|
+
On non-Windows platforms this is a no-op.
|
|
86
|
+
"""
|
|
87
|
+
if sys.platform != "win32":
|
|
88
|
+
return
|
|
89
|
+
|
|
90
|
+
# 1. Help child processes / pipes default to UTF-8
|
|
91
|
+
os.environ.setdefault("PYTHONIOENCODING", "utf-8")
|
|
92
|
+
|
|
93
|
+
# 2. Switch the console codepage to UTF-8
|
|
94
|
+
try:
|
|
95
|
+
kernel32 = ctypes.windll.kernel32 # type: ignore[attr-defined]
|
|
96
|
+
kernel32.SetConsoleOutputCP(65001)
|
|
97
|
+
kernel32.SetConsoleCP(65001)
|
|
98
|
+
except (OSError, AttributeError):
|
|
99
|
+
pass # not a real console or ctypes unavailable
|
|
100
|
+
|
|
101
|
+
# 3. Reconfigure Python streams to UTF-8
|
|
102
|
+
for name in ("stdout", "stderr"):
|
|
103
|
+
stream = getattr(sys, name, None)
|
|
104
|
+
if stream is not None and hasattr(stream, "reconfigure"):
|
|
105
|
+
try:
|
|
106
|
+
stream.reconfigure(encoding="utf-8")
|
|
107
|
+
except Exception:
|
|
108
|
+
try:
|
|
109
|
+
stream.reconfigure(encoding="utf-8", errors="backslashreplace")
|
|
110
|
+
except Exception:
|
|
111
|
+
pass
|
|
112
|
+
|
|
113
|
+
|
|
70
114
|
def main():
|
|
71
115
|
"""Main entry point for the CLI."""
|
|
116
|
+
_configure_encoding()
|
|
72
117
|
try:
|
|
73
118
|
cli(obj={})
|
|
74
119
|
except Exception as e:
|
|
@@ -120,7 +120,7 @@ def _build_expected_install_paths(declared_deps, lockfile, apm_modules_dir: Path
|
|
|
120
120
|
install_path = dep.get_install_path(apm_modules_dir)
|
|
121
121
|
try:
|
|
122
122
|
relative_path = install_path.relative_to(apm_modules_dir)
|
|
123
|
-
expected.add(
|
|
123
|
+
expected.add(relative_path.as_posix())
|
|
124
124
|
except ValueError:
|
|
125
125
|
expected.add(str(install_path))
|
|
126
126
|
|
|
@@ -136,7 +136,7 @@ def _build_expected_install_paths(declared_deps, lockfile, apm_modules_dir: Path
|
|
|
136
136
|
install_path = dep_ref.get_install_path(apm_modules_dir)
|
|
137
137
|
try:
|
|
138
138
|
relative_path = install_path.relative_to(apm_modules_dir)
|
|
139
|
-
expected.add(
|
|
139
|
+
expected.add(relative_path.as_posix())
|
|
140
140
|
except ValueError:
|
|
141
141
|
pass
|
|
142
142
|
return expected
|
|
@@ -185,11 +185,11 @@ def _check_orphaned_packages():
|
|
|
185
185
|
|
|
186
186
|
try:
|
|
187
187
|
from ..models.apm_package import APMPackage
|
|
188
|
-
from ..deps.lockfile import LockFile
|
|
188
|
+
from ..deps.lockfile import LockFile, get_lockfile_path
|
|
189
189
|
|
|
190
190
|
apm_package = APMPackage.from_apm_yml(Path("apm.yml"))
|
|
191
191
|
declared_deps = apm_package.get_apm_dependencies()
|
|
192
|
-
lockfile = LockFile.read(Path.cwd()
|
|
192
|
+
lockfile = LockFile.read(get_lockfile_path(Path.cwd()))
|
|
193
193
|
expected = _build_expected_install_paths(declared_deps, lockfile, apm_modules_dir)
|
|
194
194
|
except Exception:
|
|
195
195
|
return []
|