cf-bootstrap-instance 0.1.8__py3-none-any.whl

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.
@@ -0,0 +1,11 @@
1
+ """Stable Cogniflow instance bootstrap package."""
2
+
3
+ from pathlib import Path
4
+
5
+
6
+ def semantics_dir() -> Path:
7
+ return Path(__file__).resolve().parent / "semantics"
8
+
9
+
10
+ def semantic_files() -> tuple[Path, ...]:
11
+ return (semantics_dir() / "package.trig", semantics_dir() / "service.trig")
@@ -0,0 +1,135 @@
1
+ """Target-side JSON-lines handoff to the installed MCP service client."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ import sys
7
+ from pathlib import Path
8
+
9
+ from .transition_authority import classify, recover, rebind
10
+ from .uninstall_authority import classify_uninstall
11
+
12
+ CAPABILITY = "urn:cf:service:BootstrapInstallationHandoff"
13
+ PROFILED_OPERATION = "urn:cf:service:accept_profiled_bootstrap_handoff"
14
+ PROFILED_CAPABILITY = "urn:cf:service:ProfiledBootstrapInstallationHandoff"
15
+ PYPI_PROFILED_CAPABILITY = "urn:cf:service:PublicPyPIProfiledBootstrapInstallationHandoff"
16
+ _AUTHORITY_REQUEST = "cf.bootstrap.profile-transition-authority.request.v1"
17
+ _AUTHORITY_RESULT = "cf.bootstrap.profile-transition-authority.result.v1"
18
+ _UNINSTALL_BINDING_FIELDS = {"instance_id", "profile", "installation_root", "target_python", "cogniflow_home", "accepted_digests"}
19
+
20
+
21
+ def _uninstall_reauthentication(request: dict[str, object]) -> dict[str, object]:
22
+ if set(request) != {"schema", "target_python", "cogniflow_home", "expected"} or any(not isinstance(request.get(key), str) for key in ("target_python", "cogniflow_home")):
23
+ raise ValueError("invalid uninstall reauthentication request")
24
+ expected = request.get("expected")
25
+ if not isinstance(expected, dict) or set(expected) != _UNINSTALL_BINDING_FIELDS:
26
+ raise ValueError("invalid uninstall reauthentication binding")
27
+ digests = expected.get("accepted_digests")
28
+ if (expected.get("target_python") != request["target_python"] or expected.get("cogniflow_home") != request["cogniflow_home"]
29
+ or not isinstance(digests, dict) or not digests
30
+ or any(not isinstance(value, str) or len(value) != 71 or not value.startswith("sha256:") or any(character not in "0123456789abcdef" for character in value[7:]) for value in digests.values())):
31
+ raise ValueError("invalid uninstall reauthentication binding")
32
+ classified = classify_uninstall(Path(request["target_python"]), Path(request["cogniflow_home"]))
33
+ authority = {key: classified[key] for key in _UNINSTALL_BINDING_FIELDS}
34
+ if authority != expected:
35
+ raise ValueError("accepted instance no longer matches uninstall authorization")
36
+ return authority
37
+
38
+
39
+ def _authority_request(request: dict[str, object]) -> dict[str, object] | None:
40
+ if request.get("schema") != _AUTHORITY_REQUEST:
41
+ return None
42
+ operation = request.get("operation")
43
+ basic = {"schema", "operation", "installation_root"}
44
+ rebind_fields = basic | {"current", "target", "marker", "handoff", "manifest", "recovery", "target_handoff", "target_manifest", "target_recovery", "target_marker"}
45
+ expected = rebind_fields if operation == "rebind" else basic
46
+ if operation not in {"classify", "recover", "rebind"} or set(request) != expected:
47
+ raise ValueError("invalid profile transition authority request")
48
+ root_value = request["installation_root"]
49
+ if not isinstance(root_value, str) or not root_value or not Path(root_value).is_absolute() or Path(root_value) == Path(Path(root_value).anchor):
50
+ raise ValueError("invalid profile transition authority root")
51
+ root = Path(root_value).resolve()
52
+ try:
53
+ if operation == "classify":
54
+ status = classify(root)
55
+ elif operation == "recover":
56
+ status = recover(root)
57
+ else:
58
+ mappings = {name: request[name] for name in rebind_fields - basic}
59
+ if any(not isinstance(value, dict) for value in mappings.values()):
60
+ raise ValueError("profile transition authority values must be objects")
61
+ rebind(root, mappings["current"], mappings["target"], marker=mappings["marker"], handoff=mappings["handoff"], manifest=mappings["manifest"], recovery=mappings["recovery"], target_handoff=mappings["target_handoff"], target_manifest=mappings["target_manifest"], target_recovery=mappings["target_recovery"], target_marker=mappings["target_marker"])
62
+ status = "complete-b"
63
+ except ValueError as error:
64
+ return {"schema": _AUTHORITY_RESULT, "operation": operation, "error": {"code": "AUTHORITY_INVALID", "message": str(error)}}
65
+ return {"schema": _AUTHORITY_RESULT, "operation": operation, "status": status}
66
+
67
+
68
+ def main() -> int:
69
+ for line in sys.stdin:
70
+ request = json.loads(line)
71
+ if not isinstance(request, dict):
72
+ raise ValueError("invalid handoff request")
73
+ if request.get("schema") == "cf.bootstrap.uninstall-reauthentication.request.v1":
74
+ try:
75
+ authority = _uninstall_reauthentication(request)
76
+ response = {"schema": "cf.bootstrap.uninstall-reauthentication.result.v1", "status": "accepted", "authority": authority}
77
+ except ValueError as error:
78
+ response = {"schema": "cf.bootstrap.uninstall-reauthentication.result.v1", "status": "rejected", "error": {"code": "AUTHORITY_INVALID", "message": str(error)}}
79
+ sys.stdout.write(json.dumps(response, sort_keys=True, separators=(",", ":")) + "\n")
80
+ sys.stdout.flush()
81
+ continue
82
+ if request.get("schema") == "cf.bootstrap.uninstall-authority.request.v1":
83
+ if set(request) != {"schema", "target_python", "cogniflow_home"} or any(not isinstance(request.get(key), str) for key in ("target_python", "cogniflow_home")):
84
+ raise ValueError("invalid uninstall authority request")
85
+ try:
86
+ authority = classify_uninstall(Path(request["target_python"]), Path(request["cogniflow_home"]))
87
+ response = {"schema": "cf.bootstrap.uninstall-authority.result.v1", "status": "accepted", "authority": authority}
88
+ except ValueError as error:
89
+ response = {"schema": "cf.bootstrap.uninstall-authority.result.v1", "status": "rejected", "error": {"code": "AUTHORITY_INVALID", "message": str(error)}}
90
+ sys.stdout.write(json.dumps(response, sort_keys=True, separators=(",", ":")) + "\n")
91
+ sys.stdout.flush()
92
+ continue
93
+ authority_result = _authority_request(request)
94
+ if authority_result is not None:
95
+ sys.stdout.write(json.dumps(authority_result, sort_keys=True, separators=(",", ":")) + "\n")
96
+ sys.stdout.flush()
97
+ continue
98
+ if request.get("schema") in {"cf.bootstrap.service-handoff.request.v2", "cf.bootstrap.service-handoff.request.v3"}:
99
+ local_fields = {"schema", "instance_id", "installation_root", "cogniflow_home", "checkout_path", "plan_digest", "artifact_set_digest", "profile", "profile_digest", "selection_digest", "post_handoff_artifact_root", "post_handoff_artifact_set_digest", "post_handoff_selection_digest", "post_handoff_binding_digest", "post_handoff_profile_digest", "post_handoff_catalog_digest", "post_handoff_repository_source_digest", "post_handoff_index_digest"}
100
+ pypi_fields = {"schema", "instance_id", "installation_root", "cogniflow_home", "artifact_source", "plan_digest", "artifact_set_digest", "profile", "selection_digest", "post_handoff_artifact_root", "post_handoff_artifact_set_digest", "post_handoff_selection_digest", "post_handoff_binding_digest"}
101
+ fields = pypi_fields if request["schema"].endswith(".v3") else local_fields
102
+ if set(request) != fields:
103
+ raise ValueError("invalid profiled handoff request")
104
+ service_client = __import__("cf_" + "service_client", fromlist=["ServiceClient"])
105
+ client = service_client.ServiceClient.for_runtime("production", home=request["cogniflow_home"])
106
+ capability = PYPI_PROFILED_CAPABILITY if request["schema"].endswith(".v3") else PROFILED_CAPABILITY
107
+ result = client.call_service(capability, inputs={key: request[key] for key in fields if key != "schema"})
108
+ if result.is_error or not isinstance(result.content, dict) or result.content.get("ok") is not True:
109
+ raise RuntimeError(f"installer service did not accept the profiled handoff: {result.content}")
110
+ outputs = result.content.get("outputs", []) if isinstance(result.content, dict) else []
111
+ record = next((item.get("value", {}).get("data") for item in outputs if isinstance(item, dict) and item.get("key") == "accepted_handoff_record"), None)
112
+ if not isinstance(record, dict):
113
+ raise RuntimeError("handoff service did not return its persisted accepted record")
114
+ sys.stdout.write(json.dumps({"schema": "cf.bootstrap.service-handoff.result.v2", "status": "accepted", "profile": request["profile"], "profile_digest": request.get("profile_digest"), "selection_digest": request["selection_digest"], "handoff_receipt": record, "content": result.content}, sort_keys=True, separators=(",", ":")) + "\n")
115
+ sys.stdout.flush()
116
+ continue
117
+ if set(request) != {"schema", "cogniflow_home", "instance_id", "installation_root", "plan_digest", "artifact_set_digest"} or request["schema"] != "cf.bootstrap.service-handoff.request.v1":
118
+ raise ValueError("invalid handoff request")
119
+ service_client = __import__("cf_" + "service_client", fromlist=["ServiceClient"])
120
+ client = service_client.ServiceClient.for_runtime("production", home=request["cogniflow_home"])
121
+ result = client.call_service(CAPABILITY, inputs={key: request[key] for key in ("instance_id", "installation_root", "plan_digest", "artifact_set_digest")})
122
+ if result.is_error:
123
+ raise RuntimeError(str(result.content))
124
+ if not isinstance(result.content, dict) or set(result.content) != {"protocol", "ok", "outputs", "diagnostics", "metadata"} or result.content.get("protocol") != "cf.service.result.v1" or result.content.get("ok") is not True:
125
+ raise RuntimeError("service returned an invalid result envelope")
126
+ outputs = {item.get("key"): item.get("value", {}).get("data") for item in result.content["outputs"] if isinstance(item, dict) and isinstance(item.get("value"), dict)}
127
+ if outputs.get("status") != "accepted":
128
+ raise RuntimeError("installer service did not accept the handoff")
129
+ sys.stdout.write(json.dumps({"schema": "cf.bootstrap.service-handoff.result.v1", "content": result.content}, sort_keys=True, separators=(",", ":")) + "\n")
130
+ sys.stdout.flush()
131
+ return 0
132
+
133
+
134
+ if __name__ == "__main__":
135
+ raise SystemExit(main())
@@ -0,0 +1,119 @@
1
+ """Validated wheel-only target environment installation."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import hashlib
6
+ import os
7
+ from pathlib import Path
8
+ import tempfile
9
+ import sys
10
+
11
+ from .model import InstanceError, _validate_artifacts
12
+ from .process import run
13
+
14
+
15
+ def _entries(artifact_set: dict[str, object]) -> list[dict[str, object]]:
16
+ entries = artifact_set.get("artifacts")
17
+ if not isinstance(entries, list) or not entries:
18
+ raise InstanceError("ARTIFACT_INVALID", "artifact set is empty")
19
+ mode = "pypi" if isinstance(entries[0], dict) and "requested" in entries[0] else "local-repository"
20
+ _validate_artifacts(entries, mode)
21
+ return entries
22
+
23
+
24
+ def requirements(wheelhouse: Path, artifact_set: dict[str, object], temp_dir: Path | None = None) -> Path:
25
+ lines: list[str] = []
26
+ for item in _entries(artifact_set):
27
+ wheel = wheelhouse / item["filename"]
28
+ if wheel.is_symlink() or not wheel.is_file() or wheel.stat().st_size != item["size_bytes"] or hashlib.sha256(wheel.read_bytes()).hexdigest() != item["sha256"]:
29
+ raise InstanceError("ARTIFACT_INVALID", f"wheel validation failed: {item['filename']}")
30
+ lines.append(f"{item['normalized_distribution']}=={item['version']} --hash=sha256:{item['sha256']}")
31
+ fd, name = tempfile.mkstemp(prefix="cf-bootstrap-", suffix=".lock", dir=str(temp_dir) if temp_dir else None)
32
+ try:
33
+ with os.fdopen(fd, "w", encoding="utf-8", newline="\n") as stream:
34
+ stream.write("\n".join(lines) + "\n")
35
+ stream.flush()
36
+ os.fsync(stream.fileno())
37
+ except BaseException:
38
+ try:
39
+ os.close(fd)
40
+ except OSError:
41
+ pass
42
+ Path(name).unlink(missing_ok=True)
43
+ raise
44
+ return Path(name)
45
+
46
+
47
+ def create_environment(environment: Path, temp_root: Path | None = None) -> Path:
48
+ run([sys.executable, "-m", "venv", "--copies", str(environment)], timeout=120, temp_root=temp_root)
49
+ executable = environment / ("Scripts/python.exe" if sys.platform == "win32" else "bin/python")
50
+ if not executable.is_file():
51
+ raise InstanceError("VENV_INVALID", "venv interpreter was not created")
52
+ return executable
53
+
54
+
55
+ def install(executable: Path, wheelhouse: Path, artifact_set: dict[str, object], temp_dir: Path | None = None) -> None:
56
+ lock = requirements(wheelhouse, artifact_set, temp_dir)
57
+ try:
58
+ count_file = os.environ.get("CF_BOOTSTRAP_TEST_PIP_COUNT_FILE")
59
+ if count_file:
60
+ count = Path(count_file)
61
+ count.write_text(str(int(count.read_text(encoding="utf-8") if count.exists() else "0") + 1), encoding="utf-8")
62
+ run([str(executable), "-m", "pip", "--isolated", "install", "--no-index", "--find-links", str(wheelhouse), "--require-hashes", "--no-deps", "--no-cache-dir", "--disable-pip-version-check", "--no-input", "--requirement", str(lock)], timeout=300, temp_root=temp_dir)
63
+ finally:
64
+ lock.unlink(missing_ok=True)
65
+
66
+
67
+ _LAUNCHER_CHECK = r'''
68
+ from importlib import metadata
69
+ from pathlib import Path
70
+ from pip._vendor.distlib.scripts import ScriptMaker
71
+ import os
72
+ import sys
73
+
74
+ scripts = Path(sys.argv[1]).resolve()
75
+ final_executable = Path(sys.argv[2]).resolve()
76
+ staging_executable = Path(sys.argv[3]).resolve()
77
+ entries = []
78
+ names = set()
79
+ for distribution in metadata.distributions():
80
+ for entry in distribution.entry_points:
81
+ if entry.group != "console_scripts":
82
+ continue
83
+ if entry.name in names:
84
+ raise RuntimeError("duplicate console script: " + entry.name)
85
+ names.add(entry.name)
86
+ entries.append(entry.name + " = " + entry.value)
87
+ maker = ScriptMaker(str(scripts), str(scripts), add_launchers=True)
88
+ maker.executable = str(final_executable)
89
+ maker.clobber = True
90
+ maker.variants = {""}
91
+ written = maker.make_multiple(sorted(entries))
92
+ written_names = {Path(item).stem for item in written}
93
+ if written_names != names:
94
+ raise RuntimeError("launcher set differs from console-script metadata")
95
+ final_bytes = str(final_executable).encode()
96
+ staging_bytes = str(staging_executable).encode()
97
+ for item in written:
98
+ path = Path(item)
99
+ data = path.read_bytes()
100
+ if staging_bytes in data:
101
+ raise RuntimeError("launcher contains staging interpreter path: " + str(path))
102
+ if os.name == "nt":
103
+ if final_bytes not in data and final_bytes.replace(b"\\", b"/") not in data:
104
+ raise RuntimeError("Windows launcher is not bound to final interpreter: " + str(path))
105
+ elif data.splitlines()[0] != b"#!" + str(final_executable).encode():
106
+ raise RuntimeError("POSIX launcher is not bound to final interpreter: " + str(path))
107
+ '''
108
+
109
+
110
+ def prepare_launchers(staging_executable: Path, final_executable: Path) -> None:
111
+ """Generate deterministic final-path-bound launchers inside staging."""
112
+ scripts = staging_executable.parent
113
+ run([str(staging_executable), "-c", _LAUNCHER_CHECK, str(scripts), str(final_executable), str(staging_executable)], timeout=60)
114
+
115
+
116
+ def remove_build_tools(executable: Path) -> None:
117
+ # pip is only an installation tool; leaving it would violate the exact
118
+ # validated-distribution inventory of the non-editable instance.
119
+ run([str(executable), "-m", "pip", "--isolated", "uninstall", "--yes", "pip", "setuptools", "wheel"], timeout=60)
@@ -0,0 +1,156 @@
1
+ """External instance layout validation and atomic marker publication."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ import os
7
+ from pathlib import Path
8
+ import tempfile
9
+ import re
10
+ import stat
11
+
12
+ from .model import InstanceError, MARKER_SCHEMA, MARKER_SCHEMA_V2, _IDENTIFIER, _VERSION, canonical_json, normalize_distribution
13
+
14
+
15
+ def validate_root(root: Path, checkout: Path | None, wheelhouse: Path, home: Path) -> None:
16
+ root = _lexical_absolute(root, "installation_root")
17
+ wheelhouse = _lexical_absolute(wheelhouse, "wheelhouse_path")
18
+ home = _lexical_absolute(home, "cogniflow_home")
19
+ checkout = _lexical_absolute(checkout, "checkout_path") if checkout is not None else None
20
+ _assert_link_free(root, "installation_root")
21
+ _assert_link_free(root.parent, "installation_root parent")
22
+ _assert_link_free(wheelhouse, "wheelhouse_path")
23
+ _assert_link_free(wheelhouse.parent, "wheelhouse parent")
24
+ _assert_link_free(home, "cogniflow_home")
25
+ _assert_link_free(home.parent, "cogniflow_home parent")
26
+ if checkout is not None:
27
+ _assert_link_free(checkout, "checkout_path")
28
+ _assert_link_free(checkout.parent, "checkout parent")
29
+ if root == Path(root.anchor):
30
+ raise InstanceError("ROOT_INVALID", "installation root must be a real non-root directory")
31
+ root_canonical = root.resolve(strict=False)
32
+ wheelhouse_canonical = wheelhouse.resolve(strict=False)
33
+ home_canonical = home.resolve(strict=False)
34
+ checkout_canonical = checkout.resolve(strict=False) if checkout is not None else None
35
+ if checkout_canonical is not None and (_under(wheelhouse_canonical, checkout_canonical) or _under(checkout_canonical, wheelhouse_canonical)):
36
+ raise InstanceError("ROOT_INVALID", "wheelhouse and checkout must be disjoint")
37
+ if root_canonical == Path(root_canonical.anchor):
38
+ raise InstanceError("ROOT_INVALID", "installation root must be a real non-root directory")
39
+ for protected, label in ((home_canonical, "Cogniflow home"), (wheelhouse_canonical, "artifact output"), (checkout_canonical, "checkout")):
40
+ if protected is not None and (_under(root_canonical, protected) or _under(protected, root_canonical)):
41
+ raise InstanceError("ROOT_INVALID", f"installation root must be disjoint from {label}")
42
+ if root.exists() and not root.is_dir():
43
+ raise InstanceError("ROOT_INVALID", "installation root is not a directory")
44
+
45
+
46
+ def _lexical_absolute(path: Path | None, label: str) -> Path:
47
+ if path is None:
48
+ raise InstanceError("SCHEMA_INVALID", f"{label} is missing")
49
+ value = os.fspath(path)
50
+ if not isinstance(value, str) or not value or value != value.strip() or "\x00" in value or any(ord(char) < 32 for char in value) or ".." in Path(value).parts:
51
+ raise InstanceError("SCHEMA_INVALID", f"{label} is unsafe")
52
+ lexical = Path(os.path.abspath(value))
53
+ if not lexical.is_absolute() or lexical == Path(lexical.anchor):
54
+ raise InstanceError("SCHEMA_INVALID", f"{label} must be an absolute non-root path")
55
+ return lexical
56
+
57
+
58
+ def _components(path: Path) -> tuple[Path, ...]:
59
+ current = path
60
+ result: list[Path] = []
61
+ while current != Path(current.anchor):
62
+ result.append(current)
63
+ current = current.parent
64
+ result.reverse()
65
+ return tuple(result)
66
+
67
+
68
+ def _is_redirecting_directory(path: Path) -> bool:
69
+ if path.is_symlink():
70
+ return True
71
+ if os.name == "nt" and path.exists():
72
+ try:
73
+ attributes = os.lstat(path).st_file_attributes
74
+ except OSError as error:
75
+ raise InstanceError("ROOT_INVALID", f"cannot inspect path component: {path}") from error
76
+ return bool(attributes & stat.FILE_ATTRIBUTE_REPARSE_POINT)
77
+ return False
78
+
79
+
80
+ def _assert_link_free(path: Path, label: str) -> None:
81
+ for component in _components(path):
82
+ if component.is_symlink() or (component.exists() and _is_redirecting_directory(component)):
83
+ raise InstanceError("ROOT_INVALID", f"{label} contains a symbolic link or redirecting reparse point")
84
+
85
+
86
+ def _under(left: Path, right: Path) -> bool:
87
+ try:
88
+ left.relative_to(right)
89
+ return True
90
+ except ValueError:
91
+ return False
92
+
93
+
94
+ def atomic_write(path: Path, data: bytes) -> None:
95
+ path.parent.mkdir(parents=True, exist_ok=True)
96
+ fd, temporary = tempfile.mkstemp(prefix=f".{path.name}.", dir=path.parent)
97
+ try:
98
+ with os.fdopen(fd, "wb") as stream:
99
+ stream.write(data)
100
+ stream.flush()
101
+ os.fsync(stream.fileno())
102
+ Path(temporary).replace(path)
103
+ except OSError:
104
+ try:
105
+ Path(temporary).unlink()
106
+ except OSError:
107
+ pass
108
+ raise
109
+
110
+
111
+ def read_marker(path: Path) -> tuple[dict[str, object], bytes]:
112
+ expected_v1 = {"schema", "instance_id", "installation_root", "environment_root", "python_executable", "plan_digest", "artifact_set_digest", "installed_distributions", "state"}
113
+ expected_v2 = {"schema", "instance_id", "installation_root", "environment_root", "python_executable", "profile", "plan_digest", "profile_digest", "catalog_digest", "selection_digest", "artifact_set_digest", "installed_distributions", "recovery_manifest_path", "recovery_manifest_digest"}
114
+ try:
115
+ before = os.lstat(path)
116
+ if not stat.S_ISREG(before.st_mode) or path.is_symlink() or (os.name != "nt" and before.st_mode & 0o077):
117
+ raise OSError("marker is not a regular safe file")
118
+ descriptor = os.open(os.fspath(path), os.O_RDONLY | getattr(os, "O_NOFOLLOW", 0))
119
+ try:
120
+ raw = os.read(descriptor, 16 * 1024 * 1024)
121
+ finally:
122
+ os.close(descriptor)
123
+ value = json.loads(raw)
124
+ except (OSError, ValueError) as error:
125
+ raise InstanceError("MARKER_INVALID", "instance marker is unreadable") from error
126
+ schema = value.get("schema") if isinstance(value, dict) else None
127
+ expected = expected_v2 if schema == MARKER_SCHEMA_V2 else expected_v1
128
+ if not isinstance(value, dict) or set(value) != expected or (schema == MARKER_SCHEMA and value.get("state") != "ready") or schema not in {MARKER_SCHEMA, MARKER_SCHEMA_V2} or raw != canonical_json(value):
129
+ raise InstanceError("MARKER_INVALID", "instance marker is not canonical")
130
+ for key in ("instance_id",):
131
+ if not isinstance(value[key], str) or not _IDENTIFIER.fullmatch(value[key]):
132
+ raise InstanceError("MARKER_INVALID", f"marker {key} is invalid")
133
+ for key in ("installation_root", "environment_root", "python_executable"):
134
+ if not isinstance(value[key], str):
135
+ raise InstanceError("MARKER_INVALID", f"marker {key} is invalid")
136
+ marker_path = Path(value[key])
137
+ if not marker_path.is_absolute() or ".." in marker_path.parts or marker_path == Path(marker_path.anchor):
138
+ raise InstanceError("MARKER_INVALID", f"marker {key} is invalid")
139
+ for key in ("plan_digest", "artifact_set_digest"):
140
+ if not isinstance(value[key], str) or not re.fullmatch(r"sha256:[0-9a-f]{64}", value[key]):
141
+ raise InstanceError("MARKER_INVALID", f"marker {key} is invalid")
142
+ if schema == MARKER_SCHEMA_V2:
143
+ if not isinstance(value["profile"], str) or not re.fullmatch(r"[A-Za-z][A-Za-z0-9+.-]*:[^\s]+", value["profile"]):
144
+ raise InstanceError("MARKER_INVALID", "marker profile is invalid")
145
+ for key in ("profile_digest", "catalog_digest", "selection_digest", "recovery_manifest_digest"):
146
+ if not isinstance(value[key], str) or not re.fullmatch(r"sha256:[0-9a-f]{64}", value[key]):
147
+ raise InstanceError("MARKER_INVALID", f"marker {key} is invalid")
148
+ installed = value["installed_distributions"]
149
+ if not isinstance(installed, list) or installed != sorted(installed, key=lambda item: item.get("name", "").lower() if isinstance(item, dict) else ""):
150
+ raise InstanceError("MARKER_INVALID", "marker inventory is not sorted")
151
+ seen: set[str] = set()
152
+ for item in installed:
153
+ if not isinstance(item, dict) or set(item) != {"name", "version"} or not isinstance(item["name"], str) or not isinstance(item["version"], str) or not _IDENTIFIER.fullmatch(item["name"]) or not _VERSION.fullmatch(item["version"]) or normalize_distribution(item["name"]) in seen:
154
+ raise InstanceError("MARKER_INVALID", "marker inventory is invalid")
155
+ seen.add(normalize_distribution(item["name"]))
156
+ return value, raw