getcodexy 1.7.1__tar.gz → 1.7.2__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.
- {getcodexy-1.7.1 → getcodexy-1.7.2}/PKG-INFO +1 -1
- {getcodexy-1.7.1 → getcodexy-1.7.2}/pyproject.toml +1 -1
- {getcodexy-1.7.1 → getcodexy-1.7.2}/src/codexy_runtime_tools/_version_data/uv.lock +1 -1
- {getcodexy-1.7.1 → getcodexy-1.7.2}/src/codexy_runtime_tools/component-manifest.json +11 -9
- {getcodexy-1.7.1 → getcodexy-1.7.2}/src/codexy_runtime_tools/component_cli.py +7 -0
- {getcodexy-1.7.1 → getcodexy-1.7.2}/src/codexy_runtime_tools/component_health.py +17 -3
- {getcodexy-1.7.1 → getcodexy-1.7.2}/src/codexy_runtime_tools/component_health_support.py +30 -14
- {getcodexy-1.7.1 → getcodexy-1.7.2}/src/codexy_runtime_tools/component_inspection.py +5 -23
- getcodexy-1.7.2/src/codexy_runtime_tools/component_inspection_host.py +33 -0
- getcodexy-1.7.2/src/codexy_runtime_tools/component_lifecycle_mcp.py +45 -0
- {getcodexy-1.7.1 → getcodexy-1.7.2}/src/codexy_runtime_tools/component_lifecycle_recovery.py +10 -8
- getcodexy-1.7.2/src/codexy_runtime_tools/component_mcp_cache.py +84 -0
- getcodexy-1.7.2/src/codexy_runtime_tools/component_mcp_materialization.py +201 -0
- {getcodexy-1.7.1 → getcodexy-1.7.2}/src/codexy_runtime_tools/component_registration_health.py +11 -28
- {getcodexy-1.7.1 → getcodexy-1.7.2}/src/codexy_runtime_tools/component_watcher_probe.py +2 -18
- {getcodexy-1.7.1 → getcodexy-1.7.2}/src/codexy_runtime_tools/github_pre_session.py +11 -2
- {getcodexy-1.7.1 → getcodexy-1.7.2}/src/codexy_runtime_tools/installer.py +4 -1
- getcodexy-1.7.2/src/codexy_runtime_tools/mcp_bootstrap.py +90 -0
- {getcodexy-1.7.1 → getcodexy-1.7.2}/src/codexy_runtime_tools/pre_session.py +8 -2
- {getcodexy-1.7.1 → getcodexy-1.7.2}/uv.lock +1 -1
- getcodexy-1.7.1/src/codexy_runtime_tools/component_watcher_materialization.py +0 -116
- {getcodexy-1.7.1 → getcodexy-1.7.2}/README.md +0 -0
- {getcodexy-1.7.1 → getcodexy-1.7.2}/build_backend.py +0 -0
- {getcodexy-1.7.1 → getcodexy-1.7.2}/src/codexy_runtime_tools/__init__.py +0 -0
- {getcodexy-1.7.1 → getcodexy-1.7.2}/src/codexy_runtime_tools/activation_transaction.py +0 -0
- {getcodexy-1.7.1 → getcodexy-1.7.2}/src/codexy_runtime_tools/cache.py +0 -0
- {getcodexy-1.7.1 → getcodexy-1.7.2}/src/codexy_runtime_tools/component_capability_observation.py +0 -0
- {getcodexy-1.7.1 → getcodexy-1.7.2}/src/codexy_runtime_tools/component_capability_probe.py +0 -0
- {getcodexy-1.7.1 → getcodexy-1.7.2}/src/codexy_runtime_tools/component_capability_probe_process.py +0 -0
- {getcodexy-1.7.1 → getcodexy-1.7.2}/src/codexy_runtime_tools/component_core_hooks.py +0 -0
- {getcodexy-1.7.1 → getcodexy-1.7.2}/src/codexy_runtime_tools/component_hook_activation.py +0 -0
- {getcodexy-1.7.1 → getcodexy-1.7.2}/src/codexy_runtime_tools/component_hook_activation_expected.py +0 -0
- {getcodexy-1.7.1 → getcodexy-1.7.2}/src/codexy_runtime_tools/component_hook_activation_host.py +0 -0
- {getcodexy-1.7.1 → getcodexy-1.7.2}/src/codexy_runtime_tools/component_hook_activation_validation.py +0 -0
- {getcodexy-1.7.1 → getcodexy-1.7.2}/src/codexy_runtime_tools/component_inspection_types.py +0 -0
- {getcodexy-1.7.1 → getcodexy-1.7.2}/src/codexy_runtime_tools/component_integrity.py +0 -0
- {getcodexy-1.7.1 → getcodexy-1.7.2}/src/codexy_runtime_tools/component_interactive_probe.py +0 -0
- {getcodexy-1.7.1 → getcodexy-1.7.2}/src/codexy_runtime_tools/component_inventory_classification.py +0 -0
- {getcodexy-1.7.1 → getcodexy-1.7.2}/src/codexy_runtime_tools/component_inventory_records.py +0 -0
- {getcodexy-1.7.1 → getcodexy-1.7.2}/src/codexy_runtime_tools/component_lifecycle.py +0 -0
- {getcodexy-1.7.1 → getcodexy-1.7.2}/src/codexy_runtime_tools/component_lifecycle_admission.py +0 -0
- {getcodexy-1.7.1 → getcodexy-1.7.2}/src/codexy_runtime_tools/component_lifecycle_finish.py +0 -0
- {getcodexy-1.7.1 → getcodexy-1.7.2}/src/codexy_runtime_tools/component_lifecycle_interlock.py +0 -0
- {getcodexy-1.7.1 → getcodexy-1.7.2}/src/codexy_runtime_tools/component_lifecycle_operation.py +0 -0
- {getcodexy-1.7.1 → getcodexy-1.7.2}/src/codexy_runtime_tools/component_lifecycle_preflight.py +0 -0
- {getcodexy-1.7.1 → getcodexy-1.7.2}/src/codexy_runtime_tools/component_lifecycle_support.py +0 -0
- {getcodexy-1.7.1 → getcodexy-1.7.2}/src/codexy_runtime_tools/component_lifecycle_terminal.py +0 -0
- {getcodexy-1.7.1 → getcodexy-1.7.2}/src/codexy_runtime_tools/component_manifest.py +0 -0
- {getcodexy-1.7.1 → getcodexy-1.7.2}/src/codexy_runtime_tools/component_manifest_dependencies.py +0 -0
- {getcodexy-1.7.1 → getcodexy-1.7.2}/src/codexy_runtime_tools/component_manifest_values.py +0 -0
- {getcodexy-1.7.1 → getcodexy-1.7.2}/src/codexy_runtime_tools/component_mcp_probe.py +0 -0
- {getcodexy-1.7.1 → getcodexy-1.7.2}/src/codexy_runtime_tools/component_observed_inventory.py +0 -0
- {getcodexy-1.7.1 → getcodexy-1.7.2}/src/codexy_runtime_tools/component_resolver.py +0 -0
- {getcodexy-1.7.1 → getcodexy-1.7.2}/src/codexy_runtime_tools/component_transaction_durability.py +0 -0
- {getcodexy-1.7.1 → getcodexy-1.7.2}/src/codexy_runtime_tools/component_transaction_identity.py +0 -0
- {getcodexy-1.7.1 → getcodexy-1.7.2}/src/codexy_runtime_tools/component_transaction_receipts.py +0 -0
- {getcodexy-1.7.1 → getcodexy-1.7.2}/src/codexy_runtime_tools/component_transaction_snapshot.py +0 -0
- {getcodexy-1.7.1 → getcodexy-1.7.2}/src/codexy_runtime_tools/component_transaction_state.py +0 -0
- {getcodexy-1.7.1 → getcodexy-1.7.2}/src/codexy_runtime_tools/component_transition_journal.py +0 -0
- {getcodexy-1.7.1 → getcodexy-1.7.2}/src/codexy_runtime_tools/component_transition_model.py +0 -0
- {getcodexy-1.7.1 → getcodexy-1.7.2}/src/codexy_runtime_tools/component_transition_receipt.py +0 -0
- {getcodexy-1.7.1 → getcodexy-1.7.2}/src/codexy_runtime_tools/component_transition_rejections.py +0 -0
- {getcodexy-1.7.1 → getcodexy-1.7.2}/src/codexy_runtime_tools/contract.py +0 -0
- {getcodexy-1.7.1 → getcodexy-1.7.2}/src/codexy_runtime_tools/github_checks.py +0 -0
- {getcodexy-1.7.1 → getcodexy-1.7.2}/src/codexy_runtime_tools/identity.py +0 -0
- {getcodexy-1.7.1 → getcodexy-1.7.2}/src/codexy_runtime_tools/local_marketplace.py +0 -0
- {getcodexy-1.7.1 → getcodexy-1.7.2}/src/codexy_runtime_tools/marketplace_identity.py +0 -0
- {getcodexy-1.7.1 → getcodexy-1.7.2}/src/codexy_runtime_tools/marketplace_repin.py +0 -0
- {getcodexy-1.7.1 → getcodexy-1.7.2}/src/codexy_runtime_tools/monolith_baseline.py +0 -0
- {getcodexy-1.7.1 → getcodexy-1.7.2}/src/codexy_runtime_tools/monolith_classifier.py +0 -0
- {getcodexy-1.7.1 → getcodexy-1.7.2}/src/codexy_runtime_tools/monolith_migration.py +0 -0
- {getcodexy-1.7.1 → getcodexy-1.7.2}/src/codexy_runtime_tools/monolith_migration_host.py +0 -0
- {getcodexy-1.7.1 → getcodexy-1.7.2}/src/codexy_runtime_tools/monolith_migration_plan.py +0 -0
- {getcodexy-1.7.1 → getcodexy-1.7.2}/src/codexy_runtime_tools/monolith_migration_state.py +0 -0
- {getcodexy-1.7.1 → getcodexy-1.7.2}/src/codexy_runtime_tools/package.py +0 -0
- {getcodexy-1.7.1 → getcodexy-1.7.2}/src/codexy_runtime_tools/package_archive.py +0 -0
- {getcodexy-1.7.1 → getcodexy-1.7.2}/src/codexy_runtime_tools/plugin_resolution.py +0 -0
- {getcodexy-1.7.1 → getcodexy-1.7.2}/src/codexy_runtime_tools/release_contract_validation.py +0 -0
- {getcodexy-1.7.1 → getcodexy-1.7.2}/src/codexy_runtime_tools/release_provenance.py +0 -0
- {getcodexy-1.7.1 → getcodexy-1.7.2}/src/codexy_runtime_tools/release_watcher_contract.py +0 -0
- {getcodexy-1.7.1 → getcodexy-1.7.2}/src/codexy_runtime_tools/runtime.py +0 -0
- {getcodexy-1.7.1 → getcodexy-1.7.2}/src/codexy_runtime_tools/runtime_configuration.py +0 -0
- {getcodexy-1.7.1 → getcodexy-1.7.2}/src/codexy_runtime_tools/source.py +0 -0
- {getcodexy-1.7.1 → getcodexy-1.7.2}/src/codexy_runtime_tools/title_policy.py +0 -0
- {getcodexy-1.7.1 → getcodexy-1.7.2}/src/codexy_runtime_tools/updater.py +0 -0
- {getcodexy-1.7.1 → getcodexy-1.7.2}/src/codexy_runtime_tools/version_lock.py +0 -0
|
@@ -5,7 +5,7 @@ backend-path = ["."]
|
|
|
5
5
|
|
|
6
6
|
[project]
|
|
7
7
|
name = "getcodexy"
|
|
8
|
-
version = "1.7.
|
|
8
|
+
version = "1.7.2"
|
|
9
9
|
description = "A Codex harness for coordinating agents through planning, implementation, verification, and review."
|
|
10
10
|
readme = { file = "README.md", content-type = "text/markdown" }
|
|
11
11
|
requires-python = ">=3.10"
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
{
|
|
30
30
|
"id": "core",
|
|
31
31
|
"plugin": "codexy",
|
|
32
|
-
"version": "1.7.
|
|
32
|
+
"version": "1.7.2",
|
|
33
33
|
"dependencies": [],
|
|
34
34
|
"asset": {
|
|
35
35
|
"pluginId": "codexy@codexy",
|
|
@@ -38,6 +38,7 @@
|
|
|
38
38
|
".codex-plugin/plugin.json",
|
|
39
39
|
"assets/codexy-icon.png",
|
|
40
40
|
".mcp.json",
|
|
41
|
+
"mcp/codexy_mcp_bootstrap.py",
|
|
41
42
|
"mcp/codexy-mcp-watcher.sh",
|
|
42
43
|
"mcp/codexy-mcp-watcher.cmd",
|
|
43
44
|
"skills/dreaming/scripts/resumable-context-capsule.sh",
|
|
@@ -49,7 +50,7 @@
|
|
|
49
50
|
{
|
|
50
51
|
"id": "github",
|
|
51
52
|
"plugin": "codexy-github",
|
|
52
|
-
"version": "1.7.
|
|
53
|
+
"version": "1.7.2",
|
|
53
54
|
"dependencies": [
|
|
54
55
|
"core"
|
|
55
56
|
],
|
|
@@ -65,7 +66,7 @@
|
|
|
65
66
|
{
|
|
66
67
|
"id": "devtools",
|
|
67
68
|
"plugin": "codexy-devtools",
|
|
68
|
-
"version": "1.7.
|
|
69
|
+
"version": "1.7.2",
|
|
69
70
|
"dependencies": [
|
|
70
71
|
"core"
|
|
71
72
|
],
|
|
@@ -74,7 +75,8 @@
|
|
|
74
75
|
"packageRoot": "plugins/codexy-devtools",
|
|
75
76
|
"requiredPaths": [
|
|
76
77
|
".codex-plugin/plugin.json",
|
|
77
|
-
".mcp.json"
|
|
78
|
+
".mcp.json",
|
|
79
|
+
"mcp/codexy_mcp_bootstrap.py"
|
|
78
80
|
]
|
|
79
81
|
}
|
|
80
82
|
}
|
|
@@ -82,27 +84,27 @@
|
|
|
82
84
|
"compatibleCombinations": [
|
|
83
85
|
{
|
|
84
86
|
"components": [],
|
|
85
|
-
"version": "1.7.
|
|
87
|
+
"version": "1.7.2"
|
|
86
88
|
},
|
|
87
89
|
{
|
|
88
90
|
"components": [
|
|
89
91
|
"core"
|
|
90
92
|
],
|
|
91
|
-
"version": "1.7.
|
|
93
|
+
"version": "1.7.2"
|
|
92
94
|
},
|
|
93
95
|
{
|
|
94
96
|
"components": [
|
|
95
97
|
"core",
|
|
96
98
|
"github"
|
|
97
99
|
],
|
|
98
|
-
"version": "1.7.
|
|
100
|
+
"version": "1.7.2"
|
|
99
101
|
},
|
|
100
102
|
{
|
|
101
103
|
"components": [
|
|
102
104
|
"core",
|
|
103
105
|
"devtools"
|
|
104
106
|
],
|
|
105
|
-
"version": "1.7.
|
|
107
|
+
"version": "1.7.2"
|
|
106
108
|
},
|
|
107
109
|
{
|
|
108
110
|
"components": [
|
|
@@ -110,7 +112,7 @@
|
|
|
110
112
|
"github",
|
|
111
113
|
"devtools"
|
|
112
114
|
],
|
|
113
|
-
"version": "1.7.
|
|
115
|
+
"version": "1.7.2"
|
|
114
116
|
}
|
|
115
117
|
]
|
|
116
118
|
}
|
|
@@ -138,6 +138,13 @@ def _human(command: str, receipt: dict[str, object]) -> str:
|
|
|
138
138
|
)
|
|
139
139
|
or "none",
|
|
140
140
|
)
|
|
141
|
+
if command in {"install", "update", "remove", "bootstrap"} and receipt.get(
|
|
142
|
+
"outcome"
|
|
143
|
+
) in {"completed", "pending-action"}:
|
|
144
|
+
return (
|
|
145
|
+
f"getcodexy {command}: {receipt['outcome']}; "
|
|
146
|
+
"start a fresh Codex task to refresh installed plugins and MCP tools"
|
|
147
|
+
)
|
|
141
148
|
if command == "doctor":
|
|
142
149
|
health = receipt.get("component_health", [])
|
|
143
150
|
summary = (
|
|
@@ -10,11 +10,12 @@ from .component_capability_probe import (
|
|
|
10
10
|
)
|
|
11
11
|
from .component_capability_observation import component_observations
|
|
12
12
|
from .component_hook_activation import ACTIVATION_STATES
|
|
13
|
+
from .component_mcp_materialization import MCP_COMPONENTS, valid_component_mcp_cache
|
|
13
14
|
from .component_health_support import (
|
|
14
15
|
_authority_valid,
|
|
16
|
+
_health_plugin,
|
|
15
17
|
_legacy_state,
|
|
16
18
|
_observed,
|
|
17
|
-
_plugin_root,
|
|
18
19
|
manifest_is_valid,
|
|
19
20
|
record_version,
|
|
20
21
|
version_relation,
|
|
@@ -31,6 +32,7 @@ def health(
|
|
|
31
32
|
admission_error: str | None,
|
|
32
33
|
host_error: bool,
|
|
33
34
|
activation: dict[str, str] | None = None,
|
|
35
|
+
codex_home=None,
|
|
34
36
|
) -> list[dict[str, object]]:
|
|
35
37
|
expected = set(recorded or ()) | set(actual)
|
|
36
38
|
return [
|
|
@@ -42,6 +44,7 @@ def health(
|
|
|
42
44
|
admission_error,
|
|
43
45
|
host_error,
|
|
44
46
|
activation,
|
|
47
|
+
codex_home,
|
|
45
48
|
)
|
|
46
49
|
for component in manifest.component_ids
|
|
47
50
|
if component in expected
|
|
@@ -56,10 +59,11 @@ def _component_health(
|
|
|
56
59
|
admission_error,
|
|
57
60
|
host_error,
|
|
58
61
|
activation,
|
|
62
|
+
codex_home,
|
|
59
63
|
):
|
|
60
64
|
record = records.get(component)
|
|
61
65
|
installed = component in actual
|
|
62
|
-
plugin =
|
|
66
|
+
plugin = _health_plugin(manifest, component, record, codex_home)
|
|
63
67
|
configured = bool(
|
|
64
68
|
installed
|
|
65
69
|
and plugin
|
|
@@ -67,11 +71,21 @@ def _component_health(
|
|
|
67
71
|
plugin, manifest.component(component).plugin, record_version(record)
|
|
68
72
|
)
|
|
69
73
|
and valid_registration(plugin, component)
|
|
74
|
+
and (
|
|
75
|
+
component not in MCP_COMPONENTS
|
|
76
|
+
or valid_component_mcp_cache(codex_home, component, manifest.version)
|
|
77
|
+
)
|
|
70
78
|
)
|
|
71
79
|
result = dict(
|
|
72
80
|
component=component,
|
|
73
81
|
state=_legacy_state(
|
|
74
|
-
manifest,
|
|
82
|
+
manifest,
|
|
83
|
+
component,
|
|
84
|
+
actual,
|
|
85
|
+
records,
|
|
86
|
+
admission_error,
|
|
87
|
+
host_error,
|
|
88
|
+
codex_home,
|
|
75
89
|
),
|
|
76
90
|
installed=installed,
|
|
77
91
|
configured=configured,
|
|
@@ -4,9 +4,13 @@ import json
|
|
|
4
4
|
import os
|
|
5
5
|
from pathlib import Path
|
|
6
6
|
|
|
7
|
+
from .component_mcp_materialization import (
|
|
8
|
+
MCP_COMPONENTS,
|
|
9
|
+
component_cache_plugin,
|
|
10
|
+
mcp_spec,
|
|
11
|
+
)
|
|
7
12
|
from .component_manifest import ComponentManifest
|
|
8
13
|
from .component_registration_health import valid_registration
|
|
9
|
-
from .component_watcher_materialization import watcher_entrypoint
|
|
10
14
|
from .component_resolver import ComponentResolutionError, compare_versions
|
|
11
15
|
|
|
12
16
|
|
|
@@ -15,17 +19,23 @@ SURFACE_PATHS = {
|
|
|
15
19
|
"agents/catalog.toml",
|
|
16
20
|
"hooks/hooks.json",
|
|
17
21
|
"skills/wiki/SKILL.md",
|
|
18
|
-
".
|
|
19
|
-
"mcp/codexy-mcp-watcher.sh",
|
|
20
|
-
"mcp/codexy-mcp-watcher.cmd",
|
|
22
|
+
*(path.as_posix() for path in mcp_spec("core").surface_paths),
|
|
21
23
|
),
|
|
22
24
|
"github": ("agents/catalog.toml", "hooks/hooks.json"),
|
|
23
|
-
"devtools": ("
|
|
25
|
+
"devtools": tuple(path.as_posix() for path in mcp_spec("devtools").surface_paths),
|
|
24
26
|
}
|
|
25
27
|
AUTHORITY_KEYS = ("authority", "artifact_authority", "artifactAuthority")
|
|
26
28
|
|
|
27
29
|
|
|
28
|
-
def _legacy_state(
|
|
30
|
+
def _legacy_state(
|
|
31
|
+
manifest,
|
|
32
|
+
component,
|
|
33
|
+
actual,
|
|
34
|
+
records,
|
|
35
|
+
admission_error,
|
|
36
|
+
host_error,
|
|
37
|
+
codex_home=None,
|
|
38
|
+
):
|
|
29
39
|
record = records.get(component)
|
|
30
40
|
if admission_error or host_error:
|
|
31
41
|
return "incompatible"
|
|
@@ -34,17 +44,13 @@ def _legacy_state(manifest, component, actual, records, admission_error, host_er
|
|
|
34
44
|
relation = version_relation(manifest, record)
|
|
35
45
|
if relation != 0:
|
|
36
46
|
return "stale" if relation < 0 else "incompatible"
|
|
37
|
-
plugin =
|
|
38
|
-
if plugin is None or not _required_files(manifest, component, plugin):
|
|
47
|
+
plugin = _health_plugin(manifest, component, record, codex_home)
|
|
48
|
+
if plugin is None or not _required_files(manifest, component, plugin, codex_home):
|
|
39
49
|
return "stale"
|
|
40
50
|
if not manifest_is_valid(
|
|
41
51
|
plugin, manifest.component(component).plugin, record_version(record)
|
|
42
52
|
) or not valid_registration(plugin, component):
|
|
43
53
|
return "incompatible"
|
|
44
|
-
if component == "devtools" and not os.access(
|
|
45
|
-
plugin / "mcp/codexy-mcp-devtools", os.X_OK
|
|
46
|
-
):
|
|
47
|
-
return "stale"
|
|
48
54
|
if component == "core" and any(
|
|
49
55
|
os.path.lexists(plugin / path)
|
|
50
56
|
for path in (".codex/lsp-client.json", "lsp", "runtime-release.json")
|
|
@@ -55,13 +61,23 @@ def _legacy_state(manifest, component, actual, records, admission_error, host_er
|
|
|
55
61
|
return "healthy"
|
|
56
62
|
|
|
57
63
|
|
|
58
|
-
def _required_files(manifest, component, plugin):
|
|
64
|
+
def _required_files(manifest, component, plugin, codex_home=None):
|
|
59
65
|
paths = (
|
|
60
66
|
manifest.component(component).asset.required_paths + SURFACE_PATHS[component]
|
|
61
67
|
)
|
|
62
68
|
return all(
|
|
63
69
|
(plugin / path).is_file() and not (plugin / path).is_symlink() for path in paths
|
|
64
|
-
)
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def _health_plugin(manifest, component, record, codex_home=None):
|
|
74
|
+
plugin = _plugin_root(record)
|
|
75
|
+
if component not in MCP_COMPONENTS or codex_home is None:
|
|
76
|
+
return plugin
|
|
77
|
+
cache_root = codex_home / "plugins" / "cache"
|
|
78
|
+
if cache_root.is_symlink() or not cache_root.is_dir():
|
|
79
|
+
return plugin
|
|
80
|
+
return component_cache_plugin(codex_home, component, manifest.version)
|
|
65
81
|
|
|
66
82
|
|
|
67
83
|
def _plugin_root(record):
|
|
@@ -6,12 +6,12 @@ from __future__ import annotations
|
|
|
6
6
|
|
|
7
7
|
import os
|
|
8
8
|
import subprocess
|
|
9
|
-
from enum import Enum
|
|
10
9
|
from pathlib import Path
|
|
11
10
|
from typing import Callable
|
|
12
11
|
|
|
13
12
|
from .component_hook_activation import HookLister, activation_for_inventory
|
|
14
13
|
from .component_health import health as _health
|
|
14
|
+
from .component_inspection_host import ProbeStage, host as _host
|
|
15
15
|
from .component_inspection_types import InspectionReport
|
|
16
16
|
from .component_inventory_classification import ComponentResolutionError
|
|
17
17
|
from .component_inventory_classification import classify_installed_inventory
|
|
@@ -19,14 +19,13 @@ from .component_manifest import ComponentManifest, load_component_manifest
|
|
|
19
19
|
from .component_observed_inventory import observe_installed_inventory
|
|
20
20
|
from .component_resolver import admit_installed_inventory, canonical_components
|
|
21
21
|
from .component_transaction_state import read_inventory
|
|
22
|
-
from .github_pre_session import trusted_codex
|
|
23
22
|
from .plugin_resolution import (
|
|
24
23
|
MarketplaceBinding,
|
|
25
24
|
marketplace_identity,
|
|
26
25
|
named_marketplace,
|
|
27
26
|
validate_local_marketplace,
|
|
28
27
|
)
|
|
29
|
-
from .pre_session import
|
|
28
|
+
from .pre_session import _json
|
|
30
29
|
from .updater import _absolute, _validate_real_path
|
|
31
30
|
|
|
32
31
|
|
|
@@ -35,12 +34,6 @@ STATUS_SCHEMA = "getcodexy.status.v1"
|
|
|
35
34
|
DOCTOR_SCHEMA = "getcodexy.doctor.v1"
|
|
36
35
|
|
|
37
36
|
|
|
38
|
-
class ProbeStage(str, Enum):
|
|
39
|
-
EXECUTABLE = "codex-executable"
|
|
40
|
-
PLUGIN_LIST = "codex-plugin-list"
|
|
41
|
-
MARKETPLACE_LIST = "codex-marketplace-list"
|
|
42
|
-
|
|
43
|
-
|
|
44
37
|
def status(
|
|
45
38
|
codex_home: str | os.PathLike[str],
|
|
46
39
|
*,
|
|
@@ -71,7 +64,8 @@ def doctor(
|
|
|
71
64
|
hook_lister: HookLister | None = None,
|
|
72
65
|
) -> dict[str, object]:
|
|
73
66
|
"""Inspect canonical managed files and return actionable repairs."""
|
|
74
|
-
|
|
67
|
+
home = _absolute(codex_home)
|
|
68
|
+
report = _inspect(home, codex, runner, hook_lister)
|
|
75
69
|
missing_requirements: list[str] = (
|
|
76
70
|
[report["host_error"]] if report["host_error"] else []
|
|
77
71
|
)
|
|
@@ -97,6 +91,7 @@ def doctor(
|
|
|
97
91
|
report["admission_error"],
|
|
98
92
|
bool(report["host_error"]),
|
|
99
93
|
report["activation"],
|
|
94
|
+
codex_home=home,
|
|
100
95
|
),
|
|
101
96
|
"source_of_truth": "installed-component-inventory",
|
|
102
97
|
"errors": report["errors"],
|
|
@@ -177,19 +172,6 @@ def _inspect(
|
|
|
177
172
|
}
|
|
178
173
|
|
|
179
174
|
|
|
180
|
-
def _host(
|
|
181
|
-
home: Path, codex: Path | None, runner: Runner | None
|
|
182
|
-
) -> tuple[Path, Runner, None] | tuple[None, None, ProbeStage]:
|
|
183
|
-
try:
|
|
184
|
-
return (
|
|
185
|
-
trusted_codex(codex or _find_codex()),
|
|
186
|
-
runner or (lambda command: _run(command, home)),
|
|
187
|
-
None,
|
|
188
|
-
)
|
|
189
|
-
except (OSError, RuntimeError, ValueError):
|
|
190
|
-
return None, None, ProbeStage.EXECUTABLE
|
|
191
|
-
|
|
192
|
-
|
|
193
175
|
def _recorded(
|
|
194
176
|
home: Path,
|
|
195
177
|
) -> tuple[tuple[str, ...] | None, dict[str, object], str | None]:
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"""Trusted host setup used by component inspection."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import subprocess
|
|
6
|
+
from enum import Enum
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from typing import Callable
|
|
9
|
+
|
|
10
|
+
from .github_pre_session import trusted_codex
|
|
11
|
+
from .pre_session import _find_codex, _run
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
Runner = Callable[[list[str]], subprocess.CompletedProcess[str]]
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class ProbeStage(str, Enum):
|
|
18
|
+
EXECUTABLE = "codex-executable"
|
|
19
|
+
PLUGIN_LIST = "codex-plugin-list"
|
|
20
|
+
MARKETPLACE_LIST = "codex-marketplace-list"
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def host(
|
|
24
|
+
home: Path, codex: Path | None, runner: Runner | None
|
|
25
|
+
) -> tuple[Path, Runner, None] | tuple[None, None, ProbeStage]:
|
|
26
|
+
try:
|
|
27
|
+
return (
|
|
28
|
+
trusted_codex(codex or _find_codex()),
|
|
29
|
+
runner or (lambda command: _run(command, home)),
|
|
30
|
+
None,
|
|
31
|
+
)
|
|
32
|
+
except (OSError, RuntimeError, ValueError):
|
|
33
|
+
return None, None, ProbeStage.EXECUTABLE
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"""MCP source and cache materialization for component lifecycle operations."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
from .component_manifest import ComponentManifest
|
|
8
|
+
from .component_mcp_materialization import (
|
|
9
|
+
MCP_COMPONENTS,
|
|
10
|
+
materialize_component_mcp,
|
|
11
|
+
materialize_component_mcp_cache,
|
|
12
|
+
)
|
|
13
|
+
from .plugin_resolution import MarketplaceBinding, marketplace_path
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def materialize_mcp_sources(
|
|
17
|
+
root: MarketplaceBinding,
|
|
18
|
+
manifest: ComponentManifest,
|
|
19
|
+
components: tuple[str, ...],
|
|
20
|
+
) -> None:
|
|
21
|
+
marketplace = marketplace_path(root)
|
|
22
|
+
for component in manifest.component_ids:
|
|
23
|
+
if component not in components or component not in MCP_COMPONENTS:
|
|
24
|
+
continue
|
|
25
|
+
plugin = marketplace / "plugins" / manifest.component(component).plugin
|
|
26
|
+
materialize_component_mcp(plugin, component, manifest.version)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def materialize_mcp_caches(
|
|
30
|
+
home: Path,
|
|
31
|
+
root: MarketplaceBinding,
|
|
32
|
+
manifest: ComponentManifest,
|
|
33
|
+
components: tuple[str, ...],
|
|
34
|
+
) -> None:
|
|
35
|
+
marketplace = marketplace_path(root)
|
|
36
|
+
for component in manifest.component_ids:
|
|
37
|
+
if component not in components or component not in MCP_COMPONENTS:
|
|
38
|
+
continue
|
|
39
|
+
source = marketplace / "plugins" / manifest.component(component).plugin
|
|
40
|
+
materialize_component_mcp_cache(
|
|
41
|
+
home,
|
|
42
|
+
component,
|
|
43
|
+
manifest.version,
|
|
44
|
+
source_plugin=source,
|
|
45
|
+
)
|
{getcodexy-1.7.1 → getcodexy-1.7.2}/src/codexy_runtime_tools/component_lifecycle_recovery.py
RENAMED
|
@@ -8,9 +8,9 @@ from typing import Callable
|
|
|
8
8
|
|
|
9
9
|
from .component_hook_activation import HookLister
|
|
10
10
|
from .component_lifecycle_finish import finish_committed
|
|
11
|
+
from .component_lifecycle_mcp import materialize_mcp_caches, materialize_mcp_sources
|
|
11
12
|
from .component_lifecycle_preflight import existing_marketplace
|
|
12
13
|
from .component_manifest import ComponentManifest
|
|
13
|
-
from .component_watcher_materialization import materialize_watcher
|
|
14
14
|
from .component_resolver import (
|
|
15
15
|
ComponentResolutionError,
|
|
16
16
|
reconcile_installed_inventory,
|
|
@@ -28,7 +28,7 @@ from .component_transaction_state import (
|
|
|
28
28
|
from .component_lifecycle_terminal import terminal
|
|
29
29
|
from .marketplace_repin import reconcile_official_marketplace_root
|
|
30
30
|
from .pre_session import _json, official_marketplace_root
|
|
31
|
-
from .plugin_resolution import MarketplaceBinding, MarketplaceIdentity
|
|
31
|
+
from .plugin_resolution import MarketplaceBinding, MarketplaceIdentity
|
|
32
32
|
|
|
33
33
|
|
|
34
34
|
Runner = Callable[[list[str]], subprocess.CompletedProcess[str]]
|
|
@@ -116,7 +116,7 @@ def rollback_or_raise(
|
|
|
116
116
|
) -> None:
|
|
117
117
|
try:
|
|
118
118
|
write_journal(home, journal.with_phase("rolling-back"))
|
|
119
|
-
restore_selection(executable, invoke, manifest, root, journal.before)
|
|
119
|
+
restore_selection(home, executable, invoke, manifest, root, journal.before)
|
|
120
120
|
if (
|
|
121
121
|
selection(manifest, list_installed(executable, invoke), root)
|
|
122
122
|
!= journal.before
|
|
@@ -161,6 +161,7 @@ def write_completed(
|
|
|
161
161
|
|
|
162
162
|
|
|
163
163
|
def restore_selection(
|
|
164
|
+
home: Path,
|
|
164
165
|
executable: Path,
|
|
165
166
|
invoke: Runner,
|
|
166
167
|
manifest: ComponentManifest,
|
|
@@ -168,12 +169,14 @@ def restore_selection(
|
|
|
168
169
|
before: tuple[str, ...],
|
|
169
170
|
) -> None:
|
|
170
171
|
current = selection(manifest, list_installed(executable, invoke), root)
|
|
172
|
+
materialize_mcp_sources(root, manifest, before)
|
|
171
173
|
for component in before:
|
|
172
174
|
if component not in current:
|
|
173
175
|
mutate(executable, invoke, "add", manifest, component)
|
|
174
176
|
for component in reversed(manifest.component_ids):
|
|
175
177
|
if component in current and component not in before:
|
|
176
178
|
mutate(executable, invoke, "remove", manifest, component)
|
|
179
|
+
materialize_mcp_caches(home, root, manifest, before)
|
|
177
180
|
|
|
178
181
|
|
|
179
182
|
def apply_forward(
|
|
@@ -197,15 +200,14 @@ def apply_forward(
|
|
|
197
200
|
manifest.version,
|
|
198
201
|
home,
|
|
199
202
|
)
|
|
200
|
-
|
|
201
|
-
materialize_watcher(marketplace_path(root) / "plugins/codexy", home)
|
|
203
|
+
materialize_mcp_sources(root, manifest, journal.target)
|
|
202
204
|
for component in adds:
|
|
203
205
|
mutate(executable, invoke, "add", manifest, component)
|
|
204
206
|
for component in removes:
|
|
205
207
|
mutate(executable, invoke, "remove", manifest, component)
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
)
|
|
208
|
+
installed = list_installed(executable, invoke)
|
|
209
|
+
materialize_mcp_caches(home, root, manifest, journal.target)
|
|
210
|
+
return verify_post_operation_inventory(manifest, installed, journal.target, root)
|
|
209
211
|
|
|
210
212
|
|
|
211
213
|
def mutate(
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"""Safe synchronization helpers for component MCP cache surfaces."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
import stat
|
|
7
|
+
import tempfile
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from typing import Iterable
|
|
10
|
+
|
|
11
|
+
from .updater import _absolute, _validate_real_path
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def _needs_executable(path: Path) -> bool:
|
|
15
|
+
return path.name != "runtime-platform.sh" and path.suffix in {"", ".sh"}
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def _valid_surface_file(path: Path, *, executable: bool) -> bool:
|
|
19
|
+
try:
|
|
20
|
+
_validate_real_path(_absolute(path), require_exists=True)
|
|
21
|
+
metadata = path.lstat()
|
|
22
|
+
except (OSError, ValueError):
|
|
23
|
+
return False
|
|
24
|
+
if not stat.S_ISREG(metadata.st_mode) or stat.S_ISLNK(metadata.st_mode):
|
|
25
|
+
return False
|
|
26
|
+
return not executable or os.name == "nt" or os.access(path, os.X_OK)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _require_directory(path: Path, label: str) -> None:
|
|
30
|
+
_validate_real_path(_absolute(path), require_exists=True)
|
|
31
|
+
metadata = path.lstat()
|
|
32
|
+
if stat.S_ISLNK(metadata.st_mode) or not stat.S_ISDIR(metadata.st_mode):
|
|
33
|
+
raise RuntimeError(f"{label} is not a regular directory: {path}")
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def _sync_surface(source: Path, target: Path, paths: Iterable[Path]) -> None:
|
|
37
|
+
_require_directory(source, "MCP source plugin")
|
|
38
|
+
_require_directory(target, "MCP cache plugin")
|
|
39
|
+
for relative in paths:
|
|
40
|
+
source_path, target_path = source / relative, target / relative
|
|
41
|
+
if not _valid_surface_file(source_path, executable=_needs_executable(relative)):
|
|
42
|
+
raise RuntimeError(f"MCP source file is missing or invalid: {source_path}")
|
|
43
|
+
_validate_real_path(_absolute(target_path.parent), require_exists=False)
|
|
44
|
+
target_path.parent.mkdir(parents=True, exist_ok=True)
|
|
45
|
+
_validate_real_path(_absolute(target_path.parent), require_exists=True)
|
|
46
|
+
if _same_file(source_path, target_path):
|
|
47
|
+
continue
|
|
48
|
+
if os.path.lexists(target_path):
|
|
49
|
+
metadata = target_path.lstat()
|
|
50
|
+
if stat.S_ISLNK(metadata.st_mode) or not stat.S_ISREG(metadata.st_mode):
|
|
51
|
+
raise RuntimeError(
|
|
52
|
+
f"MCP cache file is not a regular file: {target_path}"
|
|
53
|
+
)
|
|
54
|
+
_atomic_copy(source_path, target_path)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def _same_file(source: Path, target: Path) -> bool:
|
|
58
|
+
try:
|
|
59
|
+
target_metadata = target.lstat()
|
|
60
|
+
return (
|
|
61
|
+
stat.S_ISREG(target_metadata.st_mode)
|
|
62
|
+
and not stat.S_ISLNK(target_metadata.st_mode)
|
|
63
|
+
and source.read_bytes() == target.read_bytes()
|
|
64
|
+
and stat.S_IMODE(source.stat().st_mode)
|
|
65
|
+
== stat.S_IMODE(target_metadata.st_mode)
|
|
66
|
+
)
|
|
67
|
+
except OSError:
|
|
68
|
+
return False
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def _atomic_copy(source: Path, target: Path) -> None:
|
|
72
|
+
descriptor, temporary_name = tempfile.mkstemp(
|
|
73
|
+
prefix=f".{target.name}.", suffix=".tmp", dir=target.parent
|
|
74
|
+
)
|
|
75
|
+
temporary = Path(temporary_name)
|
|
76
|
+
try:
|
|
77
|
+
with os.fdopen(descriptor, "wb") as output:
|
|
78
|
+
output.write(source.read_bytes())
|
|
79
|
+
output.flush()
|
|
80
|
+
os.fsync(output.fileno())
|
|
81
|
+
temporary.chmod(stat.S_IMODE(source.stat().st_mode))
|
|
82
|
+
os.replace(temporary, target)
|
|
83
|
+
finally:
|
|
84
|
+
temporary.unlink(missing_ok=True)
|