@mcoda/mswarm 0.1.135-codali.2 → 0.1.135-codali.6

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.
Files changed (39) hide show
  1. package/dist/vendor/codali/cli/TerminalCommand.d.ts.map +1 -1
  2. package/dist/vendor/codali/cli/TerminalCommand.js +4 -2
  3. package/dist/vendor/codali/cli/TerminalCredentials.js +2 -2
  4. package/dist/vendor/codali/eval/TerminalWorkflowCorpus.d.ts +1 -1
  5. package/dist/vendor/codali/eval/TerminalWorkflowCorpus.js +2 -2
  6. package/dist/vendor/codali/providers/ConfiguredModelScope.d.ts +1 -0
  7. package/dist/vendor/codali/providers/ConfiguredModelScope.d.ts.map +1 -1
  8. package/dist/vendor/codali/providers/ConfiguredModelScope.js +4 -0
  9. package/dist/vendor/codali/providers/ConfiguredProvider.d.ts +2 -0
  10. package/dist/vendor/codali/providers/ConfiguredProvider.d.ts.map +1 -1
  11. package/dist/vendor/codali/providers/ConfiguredProvider.js +16 -3
  12. package/dist/vendor/codali/providers/ProviderTypes.d.ts +2 -0
  13. package/dist/vendor/codali/providers/ProviderTypes.d.ts.map +1 -1
  14. package/dist/vendor/codali/providers/RequestUsage.d.ts +11 -0
  15. package/dist/vendor/codali/providers/RequestUsage.d.ts.map +1 -0
  16. package/dist/vendor/codali/providers/RequestUsage.js +33 -0
  17. package/dist/vendor/codali/runtime/SessionController.d.ts +1 -0
  18. package/dist/vendor/codali/runtime/SessionController.d.ts.map +1 -1
  19. package/dist/vendor/codali/runtime/SessionController.js +121 -20
  20. package/dist/vendor/codali/runtime/UsageReconciliation.d.ts +28 -0
  21. package/dist/vendor/codali/runtime/UsageReconciliation.d.ts.map +1 -0
  22. package/dist/vendor/codali/runtime/UsageReconciliation.js +68 -0
  23. package/dist/vendor/codali/runtime/VerificationCommand.d.ts +2 -0
  24. package/dist/vendor/codali/runtime/VerificationCommand.d.ts.map +1 -1
  25. package/dist/vendor/codali/runtime/VerificationCommand.js +28 -0
  26. package/dist/vendor/codali/session/AtomicState.d.ts.map +1 -1
  27. package/dist/vendor/codali/session/AtomicState.js +15 -1
  28. package/dist/vendor/codali/session/SessionStoragePath.d.ts.map +1 -1
  29. package/dist/vendor/codali/session/SessionStoragePath.js +28 -16
  30. package/dist/vendor/codali/tools/shell/AsyncProcess.d.ts +3 -0
  31. package/dist/vendor/codali/tools/shell/AsyncProcess.d.ts.map +1 -1
  32. package/dist/vendor/codali/tools/shell/AsyncProcess.js +1 -1
  33. package/dist/vendor/codali/tools/shell/HostToolchain.d.ts.map +1 -1
  34. package/dist/vendor/codali/tools/shell/HostToolchain.js +4 -2
  35. package/dist/vendor/codali/tools/shell/IsolatedExecutor.d.ts.map +1 -1
  36. package/dist/vendor/codali/tools/shell/IsolatedExecutor.js +5 -1
  37. package/package.json +3 -3
  38. package/scripts/media_gpu_lock.py +2 -2
  39. package/scripts/native_load_guard.py +257 -0
@@ -22,6 +22,8 @@ export const darwinSandboxProfile = (root, scratch, writePaths, runtimes, networ
22
22
  // realpath/lstat needs ancestor metadata, not directory contents or sibling files.
23
23
  ...[...ancestors].map(parent => `(allow file-read-metadata (literal ${quoted(parent)}))`),
24
24
  '(allow file-read* (subpath "/usr") (subpath "/bin") (subpath "/sbin") (subpath "/System") (subpath "/Library") (subpath "/private/etc") (subpath "/dev"))',
25
+ // Git and other native tools open the null device read/write for discarded IO.
26
+ '(allow file-write-data (literal "/dev/null"))',
25
27
  // Apple's /bin/sh reads this system selector before starting npm scripts.
26
28
  // Permit only that selector, not other private /var files or directories.
27
29
  '(allow file-read* (literal "/private/var/select/sh") (literal "/var/select/sh"))',
@@ -101,7 +103,7 @@ export const createIsolatedExecutor = (options = {}) => {
101
103
  for (const file of writePaths)
102
104
  launch.push("--bind", file, file);
103
105
  launch.push("--chdir", cwd, "--dir", "/tmp/home", "--setenv", "HOME", "/tmp/home", "--setenv", "TMPDIR", "/tmp", "--setenv", "PATH", "/tmp/codali-bin:/usr/local/bin:/usr/bin:/bin");
104
- for (const [key, value] of Object.entries({ CARGO_HOME: "/tmp/home/cargo", XDG_CACHE_HOME: "/tmp/home/cache", PYTHONPYCACHEPREFIX: "/tmp/home/pycache", PYTHONUSERBASE: "/tmp/home/python" }))
106
+ for (const [key, value] of Object.entries({ CARGO_HOME: "/tmp/home/cargo", XDG_CACHE_HOME: "/tmp/home/cache", PYTHONPYCACHEPREFIX: "/tmp/home/pycache", PYTHONUSERBASE: "/tmp/home/python", GIT_CONFIG_NOSYSTEM: "1", GIT_CONFIG_GLOBAL: "/dev/null", GIT_ATTR_NOSYSTEM: "1" }))
105
107
  launch.push("--setenv", key, value);
106
108
  // Git hooks/config and private session state are not part of executable workspace access.
107
109
  for (const dir of [".git", ".mcoda"])
@@ -125,6 +127,8 @@ export const createIsolatedExecutor = (options = {}) => {
125
127
  HOME: privateHome, TMPDIR: scratch, PATH: `${privateBin}:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin`,
126
128
  CARGO_HOME: path.join(privateHome, "cargo"), XDG_CACHE_HOME: path.join(privateHome, "cache"),
127
129
  PYTHONPYCACHEPREFIX: path.join(privateHome, "pycache"), PYTHONUSERBASE: path.join(privateHome, "python"),
130
+ // Host Git settings can invoke external helpers or read private paths.
131
+ GIT_CONFIG_NOSYSTEM: "1", GIT_CONFIG_GLOBAL: "/dev/null", GIT_ATTR_NOSYSTEM: "1",
128
132
  });
129
133
  }
130
134
  finally {
package/package.json CHANGED
@@ -8,8 +8,8 @@
8
8
  "url": "https://github.com/bekirdag/mcoda/issues"
9
9
  },
10
10
  "dependencies": {
11
- "@mcoda/db": "0.1.135-codali.2",
12
- "@mcoda/shared": "0.1.135-codali.2",
11
+ "@mcoda/db": "0.1.135-codali.6",
12
+ "@mcoda/shared": "0.1.135-codali.6",
13
13
  "fastify": "^5.7.4",
14
14
  "undici": "^6.28.0"
15
15
  },
@@ -57,5 +57,5 @@
57
57
  },
58
58
  "type": "module",
59
59
  "types": "dist/server.d.ts",
60
- "version": "0.1.135-codali.2"
60
+ "version": "0.1.135-codali.6"
61
61
  }
@@ -34,9 +34,9 @@ class MediaGpuRecoveryRequired(MediaGpuError):
34
34
  code = "media_gpu_recovery_required"
35
35
 
36
36
 
37
- def _open_scheduler() -> int:
37
+ def _open_scheduler(lock_path: str | None = None) -> int:
38
38
  runtime_dir = Path(os.environ.get("XDG_RUNTIME_DIR", f"/run/user/{os.getuid()}"))
39
- path = os.environ.get("MCODA_MEDIA_GPU_LOCK_PATH", str(runtime_dir / "mcoda-media-gpu1.lock"))
39
+ path = lock_path or os.environ.get("MCODA_MEDIA_GPU_LOCK_PATH", str(runtime_dir / "mcoda-media-gpu1.lock"))
40
40
  fd = -1
41
41
  try:
42
42
  fd = os.open(path, os.O_CREAT | os.O_RDWR | os.O_CLOEXEC | os.O_NOFOLLOW, 0o600)
@@ -0,0 +1,257 @@
1
+ """Opt-in Linux host RAM admission for native model loading and recovery.
2
+
3
+ The embedding runtime must confirm model readiness or confirmed process death
4
+ before leaving the context. Unknown outcomes retain the shared media fence.
5
+ This module never dispatches or replays inference.
6
+ """
7
+ from __future__ import annotations
8
+
9
+ import json
10
+ import logging
11
+ import math
12
+ import os
13
+ import re
14
+ import stat
15
+ import subprocess
16
+ import sys
17
+ import threading
18
+ import time
19
+ import urllib.request
20
+ from urllib.parse import urlsplit
21
+ from contextlib import contextmanager
22
+ from pathlib import Path
23
+
24
+ try:
25
+ from .media_gpu_lock import _open_scheduler, _read_lease, _write_lease, MediaGpuLease, MediaGpuError
26
+ except ImportError:
27
+ from media_gpu_lock import _open_scheduler, _read_lease, _write_lease, MediaGpuLease, MediaGpuError
28
+
29
+
30
+ class NativeLoadError(RuntimeError):
31
+ pass
32
+
33
+
34
+ _local = threading.local()
35
+
36
+
37
+ def read_policy(path: str) -> dict:
38
+ if not isinstance(path, str) or not os.path.isabs(path):
39
+ raise NativeLoadError("native load policy must be an absolute file path")
40
+ fd = os.open(path, os.O_RDONLY | os.O_NOFOLLOW | os.O_CLOEXEC)
41
+ try:
42
+ info = os.fstat(fd)
43
+ if (not stat.S_ISREG(info.st_mode) or info.st_uid != os.geteuid()
44
+ or info.st_nlink != 1 or stat.S_IMODE(info.st_mode) & 0o077):
45
+ raise NativeLoadError("native load policy must be a private, owner-owned regular file")
46
+ data = os.read(fd, 32769)
47
+ if len(data) > 32768:
48
+ raise NativeLoadError("native load policy is too large")
49
+ policy = json.loads(data)
50
+ finally:
51
+ os.close(fd)
52
+ fields = {"schemaVersion", "lockPath", "waitSeconds", "minAvailableMiB",
53
+ "restoreReserveMiB", "serviceTimeoutSeconds", "services"}
54
+ if not isinstance(policy, dict) or set(policy) != fields or type(policy["schemaVersion"]) is not int or policy["schemaVersion"] != 1:
55
+ raise NativeLoadError("unsupported native load policy fields or version")
56
+ if not isinstance(policy["lockPath"], str) or not os.path.isabs(policy["lockPath"]):
57
+ raise NativeLoadError("native load policy requires an absolute shared lock path")
58
+ for key in ("waitSeconds", "minAvailableMiB", "restoreReserveMiB", "serviceTimeoutSeconds"):
59
+ value = policy[key]
60
+ if type(value) not in (int, float) or not math.isfinite(value) or value <= 0:
61
+ raise NativeLoadError(f"native load policy {key} must be finite and positive")
62
+ if key.endswith("Seconds") and value > 3600:
63
+ raise NativeLoadError(f"native load policy {key} exceeds one hour")
64
+ peers = policy["services"]
65
+ if not isinstance(peers, list) or len(peers) > 16:
66
+ raise NativeLoadError("native load policy services must be a bounded array")
67
+ seen = set()
68
+ for peer in peers:
69
+ if (not isinstance(peer, dict) or set(peer) != {"unit", "restoreMiB", "readyUrl"}
70
+ or not isinstance(peer["unit"], str)
71
+ or not re.fullmatch(r"[A-Za-z0-9][A-Za-z0-9_.@-]{0,240}\.service", peer["unit"])
72
+ or peer["unit"] in seen or type(peer["restoreMiB"]) is not int or peer["restoreMiB"] <= 0):
73
+ raise NativeLoadError("invalid or duplicate native load peer")
74
+ url = urlsplit(peer["readyUrl"]) if isinstance(peer["readyUrl"], str) else None
75
+ if (url is None or url.scheme != "http" or url.hostname not in ("127.0.0.1", "::1")
76
+ or url.username or url.password or url.fragment):
77
+ raise NativeLoadError("native load peer readiness must use credential-free loopback HTTP")
78
+ seen.add(peer["unit"])
79
+ return policy
80
+
81
+
82
+ def available_mib() -> int:
83
+ try:
84
+ for line in Path("/proc/meminfo").read_text().splitlines():
85
+ if line.startswith("MemAvailable:"):
86
+ fields = line.split()
87
+ if len(fields) == 3 and fields[2] == "kB":
88
+ return int(fields[1]) // 1024
89
+ except (OSError, ValueError):
90
+ pass
91
+ raise NativeLoadError("available host RAM cannot be measured")
92
+
93
+
94
+ def service_state(unit: str, timeout: float) -> str:
95
+ try:
96
+ result = subprocess.run(["/usr/bin/systemctl", "--user", "show", unit,
97
+ "--property=LoadState", "--property=ActiveState"],
98
+ check=True, capture_output=True, text=True, timeout=timeout)
99
+ except (OSError, subprocess.SubprocessError) as error:
100
+ raise NativeLoadError(f"cannot inspect native load peer: {unit}") from error
101
+ values = dict(line.split("=", 1) for line in result.stdout.splitlines() if "=" in line)
102
+ if values.get("LoadState") != "loaded":
103
+ raise NativeLoadError(f"configured native load peer is not loaded: {unit}")
104
+ return values.get("ActiveState", "unknown")
105
+
106
+
107
+ def service_action(action: str, unit: str, timeout: float) -> None:
108
+ try:
109
+ subprocess.run(["/usr/bin/systemctl", "--user", action, unit],
110
+ check=True, stdin=subprocess.DEVNULL, capture_output=True, timeout=timeout)
111
+ except (OSError, subprocess.SubprocessError) as error:
112
+ raise NativeLoadError(f"cannot {action} native load peer: {unit}") from error
113
+
114
+
115
+ def wait_ready(peer: dict, timeout: float) -> None:
116
+ class NoRedirect(urllib.request.HTTPRedirectHandler):
117
+ def redirect_request(self, req, fp, code, msg, headers, newurl):
118
+ return None
119
+ opener = urllib.request.build_opener(urllib.request.ProxyHandler({}), NoRedirect())
120
+ deadline = time.monotonic() + timeout
121
+ while time.monotonic() < deadline:
122
+ try:
123
+ with opener.open(peer["readyUrl"], timeout=min(2, max(.01, deadline - time.monotonic()))) as response:
124
+ if response.status == 200:
125
+ return
126
+ except (OSError, ValueError):
127
+ pass
128
+ if service_state(peer["unit"], min(5, timeout)) not in ("active", "activating"):
129
+ raise NativeLoadError(f"native load peer stopped during restoration: {peer['unit']}")
130
+ time.sleep(min(.1, max(0, deadline - time.monotonic())))
131
+ raise NativeLoadError(f"native load peer readiness timed out: {peer['unit']}")
132
+
133
+
134
+ class NativeLoadLease:
135
+ def __init__(self, record: MediaGpuLease):
136
+ self.record = record
137
+ self.outcome = None
138
+
139
+ def confirm_ready(self) -> None:
140
+ self.outcome = "ready"
141
+
142
+ def confirm_stopped(self) -> None:
143
+ self.outcome = "stopped"
144
+
145
+
146
+ def _restore(policy: dict, parked: list[dict]) -> None:
147
+ errors = []
148
+ for peer in parked:
149
+ try:
150
+ needed = policy["restoreReserveMiB"] + peer["restoreMiB"]
151
+ if available_mib() < needed:
152
+ raise NativeLoadError(f"insufficient host RAM to restore {peer['unit']}")
153
+ service_action("start", peer["unit"], policy["serviceTimeoutSeconds"])
154
+ if service_state(peer["unit"], policy["serviceTimeoutSeconds"]) != "active":
155
+ raise NativeLoadError(f"native load peer did not become active: {peer['unit']}")
156
+ wait_ready(peer, policy["serviceTimeoutSeconds"])
157
+ except Exception as error:
158
+ # Preserve the other original peers when one cannot restart.
159
+ errors.append(error)
160
+ if errors:
161
+ raise NativeLoadError("native load peer restoration failed; retain the admission fence") from errors[0]
162
+
163
+
164
+ @contextmanager
165
+ def native_load_guard(env: dict | None):
166
+ """Use MCODA_NATIVE_LOAD_POLICY from trusted host configuration only.
167
+
168
+ The caller must have stopped its old model process before entering. Nested
169
+ retries in the same thread retain one lease; other threads/processes wait.
170
+ No configured policy means no lock, filesystem, systemctl or RAM probes.
171
+ """
172
+ path = (env or {}).get("MCODA_NATIVE_LOAD_POLICY")
173
+ if path is None:
174
+ yield None
175
+ return
176
+ active = getattr(_local, "active", None)
177
+ if active is not None:
178
+ if active[0] != path:
179
+ raise NativeLoadError("cannot nest different native load policies")
180
+ active[1].outcome = None
181
+ yield active[1]
182
+ return
183
+ import fcntl
184
+ try:
185
+ policy = read_policy(path)
186
+ fd = _open_scheduler(policy["lockPath"])
187
+ except (OSError, ValueError, MediaGpuError) as error:
188
+ raise NativeLoadError("cannot open the native load policy or shared lease") from error
189
+ record = None
190
+ parked = []
191
+ entered_model = False
192
+ completed = False
193
+ try:
194
+ deadline = time.monotonic() + policy["waitSeconds"]
195
+ while True:
196
+ try:
197
+ fcntl.flock(fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
198
+ break
199
+ except BlockingIOError as error:
200
+ if time.monotonic() >= deadline:
201
+ raise NativeLoadError("active media still owns the native load lease") from error
202
+ time.sleep(min(0.1, max(0, deadline - time.monotonic())))
203
+ try:
204
+ previous = _read_lease(fd)
205
+ except MediaGpuError as error:
206
+ raise NativeLoadError("shared native/media lease is unreadable; reconciliation is required") from error
207
+ if previous is not None:
208
+ raise NativeLoadError("an earlier native/media outcome must be reconciled before model loading")
209
+ record = MediaGpuLease(fd, "native-model-load")
210
+ lease = NativeLoadLease(record)
211
+ _local.active = (path, lease)
212
+ for peer in policy["services"]:
213
+ state = service_state(peer["unit"], policy["serviceTimeoutSeconds"])
214
+ if state in ("inactive", "failed"):
215
+ continue
216
+ if state != "active":
217
+ raise NativeLoadError(f"native load peer is changing state: {peer['unit']}")
218
+ # Record before stop: a timed-out stop may still complete.
219
+ parked.append(peer)
220
+ record.record["parked_units"] = [item["unit"] for item in parked]
221
+ _write_lease(fd, record.record)
222
+ service_action("stop", peer["unit"], policy["serviceTimeoutSeconds"])
223
+ if service_state(peer["unit"], policy["serviceTimeoutSeconds"]) != "inactive":
224
+ raise NativeLoadError(f"native load peer has not stopped: {peer['unit']}")
225
+ free = available_mib()
226
+ if free < policy["minAvailableMiB"]:
227
+ raise NativeLoadError(f"native model needs {policy['minAvailableMiB']} MiB available RAM; measured {free}")
228
+ logging.info("native_model_load_admitted available_mib=%d parked_peers=%d", free, len(parked))
229
+ entered_model = True
230
+ try:
231
+ yield lease
232
+ finally:
233
+ # A dead caller or an unconfirmed model process must never admit
234
+ # concurrent media. Keep a durable receipt for native reconciliation.
235
+ if lease.outcome is not None:
236
+ record.mark_uncertain("native_model_peer_restore_pending")
237
+ _restore(policy, parked)
238
+ completed = True
239
+ _write_lease(fd, None)
240
+ else:
241
+ record.mark_uncertain("native_model_load_outcome_unknown")
242
+ if sys.exc_info()[0] is None:
243
+ raise NativeLoadError("native model load returned without readiness or process-death proof")
244
+ finally:
245
+ try:
246
+ if record is not None and not entered_model:
247
+ record.mark_uncertain("native_model_peer_restore_pending")
248
+ _restore(policy, parked)
249
+ completed = True
250
+ _write_lease(fd, None)
251
+ if record is not None and not completed:
252
+ record.mark_uncertain("native_model_load_or_restore_unconfirmed")
253
+ finally:
254
+ if record is not None:
255
+ record._closed = True
256
+ _local.active = None
257
+ os.close(fd)