clm-kernel 0.0.0__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.
Files changed (333) hide show
  1. clm/__init__.py +230 -0
  2. clm/__main__.py +25 -0
  3. clm/cross_cutting/__init__.py +50 -0
  4. clm/cross_cutting/_test_runner.py +36 -0
  5. clm/cross_cutting/async_utils.py +25 -0
  6. clm/cross_cutting/cel_builder.py +123 -0
  7. clm/cross_cutting/cel_converter.py +697 -0
  8. clm/cross_cutting/cel_engine.py +252 -0
  9. clm/cross_cutting/cel_ops.py +72 -0
  10. clm/cross_cutting/cel_ops_db.py +157 -0
  11. clm/cross_cutting/cel_ops_files.py +99 -0
  12. clm/cross_cutting/cel_ops_state.py +71 -0
  13. clm/cross_cutting/cel_ops_strings.py +64 -0
  14. clm/cross_cutting/cel_resolver.py +63 -0
  15. clm/cross_cutting/cel_slices.py +56 -0
  16. clm/cross_cutting/cel_store.py +330 -0
  17. clm/cross_cutting/clm_logger.py +140 -0
  18. clm/cross_cutting/config/__init__.py +155 -0
  19. clm/cross_cutting/config/config_constants.py +392 -0
  20. clm/cross_cutting/config/env_parameters.py +90 -0
  21. clm/cross_cutting/config/logging.py +378 -0
  22. clm/cross_cutting/config/settings.py +504 -0
  23. clm/cross_cutting/context.py +5 -0
  24. clm/cross_cutting/domain_models.py +55 -0
  25. clm/cross_cutting/domain_types.py +61 -0
  26. clm/cross_cutting/effects.py +67 -0
  27. clm/cross_cutting/errors.py +153 -0
  28. clm/cross_cutting/governance.py +331 -0
  29. clm/cross_cutting/guards.py +41 -0
  30. clm/cross_cutting/mcard.py +321 -0
  31. clm/cross_cutting/native.py +185 -0
  32. clm/cross_cutting/observability.py +289 -0
  33. clm/cross_cutting/pcard.py +434 -0
  34. clm/cross_cutting/registry.py +71 -0
  35. clm/cross_cutting/resources.py +84 -0
  36. clm/cross_cutting/secrets.py +44 -0
  37. clm/cross_cutting/subprocess_utils.py +118 -0
  38. clm/cross_cutting/telemetry.py +164 -0
  39. clm/cross_cutting/testing/__init__.py +8 -0
  40. clm/cross_cutting/testing/comparator.py +187 -0
  41. clm/cross_cutting/testing/datasets.py +87 -0
  42. clm/cross_cutting/timeutil.py +20 -0
  43. clm/cross_cutting/types/new_type_check.py +0 -0
  44. clm/cross_cutting/utils/__init__.py +1 -0
  45. clm/cross_cutting/utils/url_safety.py +50 -0
  46. clm/cross_cutting/vcard.py +560 -0
  47. clm/cross_cutting/writer.py +118 -0
  48. clm/layer0/README.md +50 -0
  49. clm/layer0/__init__.py +223 -0
  50. clm/layer0/algebra.py +60 -0
  51. clm/layer0/algebra_baldwin.py +133 -0
  52. clm/layer0/algebra_baldwin_generators.py +36 -0
  53. clm/layer0/algebra_equivalence.py +262 -0
  54. clm/layer0/algebra_ingest.py +306 -0
  55. clm/layer0/algebra_manifest_io.py +156 -0
  56. clm/layer0/assembler.py +481 -0
  57. clm/layer0/cel/__init__.py +23 -0
  58. clm/layer0/cel_eval.py +491 -0
  59. clm/layer0/codec.py +106 -0
  60. clm/layer0/context.py +122 -0
  61. clm/layer0/db.py +160 -0
  62. clm/layer0/exceptions.py +73 -0
  63. clm/layer0/fibration.py +106 -0
  64. clm/layer0/gates.py +50 -0
  65. clm/layer0/hash.py +80 -0
  66. clm/layer0/loader.py +826 -0
  67. clm/layer0/math_utils.py +163 -0
  68. clm/layer0/mcard.py +96 -0
  69. clm/layer0/model/__init__.py +0 -0
  70. clm/layer0/model/action.py +223 -0
  71. clm/layer0/model/card.py +280 -0
  72. clm/layer0/model/card_triad.py +427 -0
  73. clm/layer0/model/compatibility.py +60 -0
  74. clm/layer0/model/dictionary.py +46 -0
  75. clm/layer0/model/dots.py +331 -0
  76. clm/layer0/model/event_producer.py +88 -0
  77. clm/layer0/model/g_time.py +89 -0
  78. clm/layer0/model/handle.py +163 -0
  79. clm/layer0/model/hash/__init__.py +6 -0
  80. clm/layer0/model/hash/algorithms/__init__.py +3 -0
  81. clm/layer0/model/hash/algorithms/custom_hash.py +7 -0
  82. clm/layer0/model/hash/algorithms/local_sha256.py +178 -0
  83. clm/layer0/model/hash/constants.py +53 -0
  84. clm/layer0/model/hash/enums.py +60 -0
  85. clm/layer0/model/hash/validator.py +307 -0
  86. clm/layer0/model/interpreter.py +275 -0
  87. clm/layer0/model/pagination.py +45 -0
  88. clm/layer0/model/pcard.py +716 -0
  89. clm/layer0/model/schema.py +44 -0
  90. clm/layer0/model/utils/__init__.py +1 -0
  91. clm/layer0/model/utils/content_analyzer.py +152 -0
  92. clm/layer0/model/validators/__init__.py +1 -0
  93. clm/layer0/model/validators/base_validator.py +44 -0
  94. clm/layer0/model/validators/binary_validator.py +66 -0
  95. clm/layer0/model/validators/text_validator.py +82 -0
  96. clm/layer0/model/validators/validation_registry.py +49 -0
  97. clm/layer0/model/vcard.py +823 -0
  98. clm/layer0/model/vcard_ext/__init__.py +39 -0
  99. clm/layer0/model/vcard_ext/core.py +60 -0
  100. clm/layer0/model/vcard_ext/network.py +23 -0
  101. clm/layer0/model/vcard_ext/observability.py +63 -0
  102. clm/layer0/model/vcard_ext/storage.py +60 -0
  103. clm/layer0/model/vcard_ext/vendors.py +211 -0
  104. clm/layer0/model/vcard_sandwich.py +119 -0
  105. clm/layer0/model/vcard_vocabulary.py +463 -0
  106. clm/layer0/model/workflow.py +274 -0
  107. clm/layer0/narrative.py +431 -0
  108. clm/layer0/ontology/collection.py +125 -0
  109. clm/layer0/parser.py +429 -0
  110. clm/layer0/relations.py +73 -0
  111. clm/layer0/rule_evaluator.py +251 -0
  112. clm/layer0/schema/__init__.py +38 -0
  113. clm/layer0/schema/ddl.py +97 -0
  114. clm/layer0/templates/abstract.yaml +99 -0
  115. clm/layer0/templates/balanced.yaml +253 -0
  116. clm/layer0/templates/concrete.yaml +172 -0
  117. clm/layer0/type_registry.py +223 -0
  118. clm/layer0/types.py +204 -0
  119. clm/layer0/utils.py +886 -0
  120. clm/layer0/verifier_core.py +262 -0
  121. clm/layer1/__init__.py +31 -0
  122. clm/layer1/action_dispatcher.py +275 -0
  123. clm/layer1/adapter_registry.py +233 -0
  124. clm/layer1/algebra_baldwin_generators.py +192 -0
  125. clm/layer1/arrow_compiler.py +525 -0
  126. clm/layer1/batch_processor.py +152 -0
  127. clm/layer1/cel/__init__.py +27 -0
  128. clm/layer1/cel/builder.py +173 -0
  129. clm/layer1/engine.py +759 -0
  130. clm/layer1/engine_async.py +89 -0
  131. clm/layer1/eval_callback.py +49 -0
  132. clm/layer1/gate_composer.py +229 -0
  133. clm/layer1/hoare_sandwich.py +298 -0
  134. clm/layer1/identity_guard.py +348 -0
  135. clm/layer1/matrix.py +138 -0
  136. clm/layer1/neural_runtime.py +221 -0
  137. clm/layer1/operations/__init__.py +123 -0
  138. clm/layer1/operations/augment.py +156 -0
  139. clm/layer1/operations/base.py +109 -0
  140. clm/layer1/operations/builtins.py +338 -0
  141. clm/layer1/operations/dispatch.py +144 -0
  142. clm/layer1/operations/exclude.py +107 -0
  143. clm/layer1/operations/handle.py +197 -0
  144. clm/layer1/operations/identity.py +139 -0
  145. clm/layer1/operations/invert.py +66 -0
  146. clm/layer1/operations/loader.py +116 -0
  147. clm/layer1/operations/manipulators/__init__.py +23 -0
  148. clm/layer1/operations/manipulators/document.py +211 -0
  149. clm/layer1/operations/manipulators/media.py +134 -0
  150. clm/layer1/operations/manipulators/structured.py +159 -0
  151. clm/layer1/operations/manipulators/tabular.py +334 -0
  152. clm/layer1/operations/port.py +65 -0
  153. clm/layer1/operations/services.py +529 -0
  154. clm/layer1/operations/split.py +117 -0
  155. clm/layer1/operations/substitute.py +75 -0
  156. clm/layer1/petri.py +1052 -0
  157. clm/layer1/pocketflow_orchestrator.py +408 -0
  158. clm/layer1/pocketflow_scheduler.py +519 -0
  159. clm/layer1/protocols.py +197 -0
  160. clm/layer1/runtime_adapter.py +718 -0
  161. clm/layer1/runtimes/__init__.py +71 -0
  162. clm/layer1/runtimes/_generated_assertions.py +125 -0
  163. clm/layer1/runtimes/_generated_types.py +42 -0
  164. clm/layer1/runtimes/base.py +265 -0
  165. clm/layer1/runtimes/binary.py +101 -0
  166. clm/layer1/runtimes/factory.py +203 -0
  167. clm/layer1/runtimes/javascript.py +321 -0
  168. clm/layer1/runtimes/javascript_runtime.js +33 -0
  169. clm/layer1/runtimes/js_scripts/isomorphism.mjs +22 -0
  170. clm/layer1/runtimes/js_scripts/loader.mjs +52 -0
  171. clm/layer1/runtimes/lambda_calc.py +126 -0
  172. clm/layer1/runtimes/python.py +288 -0
  173. clm/layer1/runtimes/python_runtime.py +41 -0
  174. clm/layer1/runtimes/script.py +114 -0
  175. clm/layer1/sandbox.py +334 -0
  176. clm/layer1/savepoint_guard.py +136 -0
  177. clm/layer1/sparse_net.py +122 -0
  178. clm/layer1/spec_tester.py +165 -0
  179. clm/layer1/store.py +295 -0
  180. clm/layer1/test_runner.py +498 -0
  181. clm/layer1/type_inspector.py +166 -0
  182. clm/layer1/verifier.py +772 -0
  183. clm/layer1/vm.py +91 -0
  184. clm/layer2/__init__.py +78 -0
  185. clm/layer2/card_collection.py +678 -0
  186. clm/layer2/classifier.py +479 -0
  187. clm/layer2/engine/__init__.py +1 -0
  188. clm/layer2/engine/abstract_sql_engine.py +320 -0
  189. clm/layer2/engine/base.py +193 -0
  190. clm/layer2/engine/duckdb_engine.py +576 -0
  191. clm/layer2/engine/sqlite_engine.py +518 -0
  192. clm/layer2/fiber/__init__.py +31 -0
  193. clm/layer2/fiber/context.py +89 -0
  194. clm/layer2/fiber/disposable.py +163 -0
  195. clm/layer2/fiber/fiber.py +125 -0
  196. clm/layer2/fiber/hoare.py +191 -0
  197. clm/layer2/file_io.py +376 -0
  198. clm/layer2/file_registrar.py +77 -0
  199. clm/layer2/ingest.py +202 -0
  200. clm/layer2/lineage.py +322 -0
  201. clm/layer2/mcard_fs.py +262 -0
  202. clm/layer2/merkle.py +11 -0
  203. clm/layer2/mime.py +244 -0
  204. clm/layer2/mime_detector.py +245 -0
  205. clm/layer2/private_collection.py +118 -0
  206. clm/layer2/shared_store.py +11 -0
  207. clm/layer2/storage/__init__.py +190 -0
  208. clm/layer2/storage/db_bitemporal.py +141 -0
  209. clm/layer2/storage/db_driver.py +331 -0
  210. clm/layer2/storage/db_hyperlinks.py +214 -0
  211. clm/layer2/storage/db_pool.py +139 -0
  212. clm/layer2/storage/db_repository.py +229 -0
  213. clm/layer2/storage/savepoint.py +78 -0
  214. clm/layer2/storage/sqlite_wal.py +74 -0
  215. clm/layer2/tridb.py +245 -0
  216. clm/layer2/vcard_receipt.py +71 -0
  217. clm/layer2/vfs.py +17 -0
  218. clm/layer3/__init__.py +141 -0
  219. clm/layer3/agency/__init__.py +19 -0
  220. clm/layer3/agency/config.py +220 -0
  221. clm/layer3/agency/providers/__init__.py +15 -0
  222. clm/layer3/agency/providers/base.py +91 -0
  223. clm/layer3/agency/providers/mlc_llm.py +169 -0
  224. clm/layer3/agency/providers/ollama.py +203 -0
  225. clm/layer3/agency/router.py +71 -0
  226. clm/layer3/agency/runtime.py +524 -0
  227. clm/layer3/gateway.py +353 -0
  228. clm/layer3/open_interpreter_adapter.py +159 -0
  229. clm/layer3/rag/__init__.py +70 -0
  230. clm/layer3/rag/cli.py +228 -0
  231. clm/layer3/rag/config.py +153 -0
  232. clm/layer3/rag/embeddings/__init__.py +16 -0
  233. clm/layer3/rag/embeddings/base.py +79 -0
  234. clm/layer3/rag/embeddings/ollama.py +211 -0
  235. clm/layer3/rag/embeddings/vision.py +290 -0
  236. clm/layer3/rag/engine.py +276 -0
  237. clm/layer3/rag/graph/__init__.py +22 -0
  238. clm/layer3/rag/graph/community.py +198 -0
  239. clm/layer3/rag/graph/engine.py +433 -0
  240. clm/layer3/rag/graph/extractor.py +255 -0
  241. clm/layer3/rag/graph/schema.py +115 -0
  242. clm/layer3/rag/graph/store.py +641 -0
  243. clm/layer3/rag/indexer.py +333 -0
  244. clm/layer3/rag/llm_providers.json +52 -0
  245. clm/layer3/rag/semantic_versioning.py +313 -0
  246. clm/layer3/rag/vector/__init__.py +28 -0
  247. clm/layer3/rag/vector/handle_vector_store.py +749 -0
  248. clm/layer3/rag/vector/schema.py +151 -0
  249. clm/layer3/rag/vector/store.py +632 -0
  250. clm/layer3/reticulum/__init__.py +55 -0
  251. clm/layer3/reticulum/bridge.py +74 -0
  252. clm/layer3/reticulum/daemon.py +88 -0
  253. clm/layer3/reticulum/discovery.py +164 -0
  254. clm/layer3/reticulum/identity.py +107 -0
  255. clm/layer3/reticulum/media.py +148 -0
  256. clm/layer3/reticulum/transport.py +133 -0
  257. clm/layer3/satori/__init__.py +209 -0
  258. clm/layer3/satori/alpha_conversion.py +168 -0
  259. clm/layer3/satori/beta_reduction.py +359 -0
  260. clm/layer3/satori/continuation.py +38 -0
  261. clm/layer3/satori/eta_conversion.py +205 -0
  262. clm/layer3/satori/free_variables.py +163 -0
  263. clm/layer3/satori/io_effects.py +370 -0
  264. clm/layer3/satori/lambda_runtime.py +726 -0
  265. clm/layer3/satori/lambda_term.py +230 -0
  266. clm/layer3/satori/sheaf_audit.py +261 -0
  267. clm/layer3/satori/speech_act.py +279 -0
  268. clm/layer4/README.md +9 -0
  269. clm/layer4/__init__.py +67 -0
  270. clm/layer4/_sovereign_executor.py +580 -0
  271. clm/layer4/action_commands.py +719 -0
  272. clm/layer4/api.py +236 -0
  273. clm/layer4/bootstrap/__init__.py +13 -0
  274. clm/layer4/bootstrap/bootstrapper.py +118 -0
  275. clm/layer4/bootstrap/cleanup.py +44 -0
  276. clm/layer4/bootstrap/clm_hooks.py +49 -0
  277. clm/layer4/bootstrap/genesis.py +77 -0
  278. clm/layer4/bootstrap/injection.py +125 -0
  279. clm/layer4/cli.py +549 -0
  280. clm/layer4/collection_manager.py +553 -0
  281. clm/layer4/commands/__init__.py +81 -0
  282. clm/layer4/commands/action_cmd.py +42 -0
  283. clm/layer4/commands/check_cmd.py +82 -0
  284. clm/layer4/commands/envelope.py +73 -0
  285. clm/layer4/commands/evaluate_cmd.py +224 -0
  286. clm/layer4/commands/mcard_cmd.py +24 -0
  287. clm/layer4/commands/petri_cmd.py +92 -0
  288. clm/layer4/commands/tridb_cmd.py +319 -0
  289. clm/layer4/commands/version_cmd.py +27 -0
  290. clm/layer4/diff_commands.py +167 -0
  291. clm/layer4/eoa_sponsor.py +232 -0
  292. clm/layer4/events.py +69 -0
  293. clm/layer4/flux_gateway.py +133 -0
  294. clm/layer4/gateway.py +91 -0
  295. clm/layer4/loader.py +307 -0
  296. clm/layer4/mcard_commands.py +166 -0
  297. clm/layer4/membrane.py +273 -0
  298. clm/layer4/navigation.py +50 -0
  299. clm/layer4/protocol/protocol_entry_python.py +108 -0
  300. clm/layer4/protocol/python_sync.py +126 -0
  301. clm/layer4/runner.py +150 -0
  302. clm/layer4/satori.py +262 -0
  303. clm/layer4/server/python_sync_client.py +150 -0
  304. clm/layer4/server/python_sync_server.py +139 -0
  305. clm/layer4/server/service_api.py +315 -0
  306. clm/layer4/server/start_servers.py +107 -0
  307. clm/layer4/server/stop_servers.py +24 -0
  308. clm/layer4/simulated_optimization_fix.py +0 -0
  309. clm/layer4/storage.py +463 -0
  310. clm/layer4/tridb_admin.py +268 -0
  311. clm/layer4/websocket/test_ws_client.py +104 -0
  312. clm/layer4/websocket/test_ws_server.py +138 -0
  313. clm/layer4/websocket/ws_server_local.py +160 -0
  314. clm/layer5/__init__.py +51 -0
  315. clm/layer5/async_batch_writer.py +133 -0
  316. clm/layer5/learning.py +276 -0
  317. clm/layer5/telemetry_hook.py +107 -0
  318. clm/layer5/type_lattice.py +238 -0
  319. clm/py.typed +0 -0
  320. clm/schemas/__init__.py +652 -0
  321. clm/schemas/abstract_face_schema.yaml +77 -0
  322. clm/schemas/cel_schema.yaml +416 -0
  323. clm/schemas/lifecycle/__init__.py +45 -0
  324. clm/schemas/lifecycle/models.py +123 -0
  325. clm/schemas/lifecycle/service.py +364 -0
  326. clm/schemas/mcard_schema.sql +192 -0
  327. clm/schemas/mcard_vector_schema.sql +283 -0
  328. clm/schemas/mlp-context-v1.json +91 -0
  329. clm_kernel-0.0.0.dist-info/METADATA +121 -0
  330. clm_kernel-0.0.0.dist-info/RECORD +333 -0
  331. clm_kernel-0.0.0.dist-info/WHEEL +4 -0
  332. clm_kernel-0.0.0.dist-info/entry_points.txt +3 -0
  333. clm_kernel-0.0.0.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,529 @@
1
+ """
2
+ Services Operations Module
3
+ ==========================
4
+
5
+ Contains operations for managing long-running services (servers, daemons).
6
+ Moved from builtins.py to separate concerns.
7
+ """
8
+
9
+ from typing import Any
10
+
11
+ from clm.cross_cutting.config.config_constants import (
12
+ DEFAULT_WS_HOST,
13
+ DEFAULT_WS_PORT,
14
+ ENV_WS_PORT,
15
+ ENV_WS_PORT_LEGACY,
16
+ SIGNALING_PORT_RELEASE_WAIT_SECS,
17
+ )
18
+ from clm.layer0.model.card import MCard
19
+
20
+ NETWORK_DEFAULT_HOST = DEFAULT_WS_HOST
21
+ PROCESS_TERMINATE_WAIT_SECONDS = SIGNALING_PORT_RELEASE_WAIT_SECS
22
+ STATIC_SERVER_START_WAIT_SECONDS = 1.5
23
+ WEBSOCKET_SERVER_START_WAIT_SECONDS = 3.0
24
+
25
+
26
+ def op_static_server(impl: dict, target: MCard, ctx: dict) -> Any:
27
+ """
28
+ Execute static_server builtin - manages HTTP static file server.
29
+
30
+ Actions: deploy, status, stop
31
+ Config: root_dir, port, host
32
+ """
33
+ import os
34
+ import signal
35
+ import socket
36
+ import subprocess
37
+ import time
38
+
39
+ def _resolve_config_value(val: Any) -> Any:
40
+ """Resolve environment variables in config values like '${VAR}'."""
41
+ if isinstance(val, str) and val.startswith("${") and val.endswith("}"):
42
+ env_var = val[2:-1]
43
+ return os.getenv(env_var, val)
44
+ return val
45
+
46
+ # Valid config keys: port, host, root_dir
47
+ config = impl.get("config", {})
48
+ action = ctx.get("action") or config.get("action", "status")
49
+
50
+ # Default to HTTP_PORT from .env matching the project configuration
51
+ default_port = int(os.getenv("HTTP_PORT", 5320))
52
+ raw_port = ctx.get("port") or config.get("port", default_port)
53
+ port = int(_resolve_config_value(raw_port))
54
+
55
+ host = ctx.get("host") or config.get("host", NETWORK_DEFAULT_HOST)
56
+ root_dir = ctx.get("root_dir") or config.get("root_dir", ".")
57
+
58
+ # Resolve root_dir relative to project root
59
+ project_root = os.getcwd()
60
+ if not os.path.isabs(root_dir):
61
+ root_dir = os.path.normpath(os.path.join(project_root, root_dir))
62
+
63
+ pid_file = os.path.join(project_root, f".static_server_{port}.pid")
64
+
65
+ def is_port_in_use(p: int) -> bool:
66
+ # Use lsof to check if port is in use (handles both IPv4 and IPv6)
67
+ try:
68
+ result = subprocess.run(
69
+ ["lsof", "-ti", f":{p}"], capture_output=True, text=True
70
+ )
71
+ return result.returncode == 0 and result.stdout.strip() != ""
72
+ except (OSError, subprocess.SubprocessError):
73
+ # Fallback to socket check
74
+ with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
75
+ return s.connect_ex(("localhost", p)) == 0
76
+
77
+ def get_pid_on_port(p: int):
78
+ try:
79
+ result = subprocess.run(
80
+ ["lsof", "-ti", f":{p}"], capture_output=True, text=True
81
+ )
82
+ if result.returncode == 0 and result.stdout.strip():
83
+ return int(result.stdout.strip().split("\n")[0])
84
+ except (OSError, subprocess.SubprocessError, ValueError):
85
+ pass
86
+ return None
87
+
88
+ if action == "deploy":
89
+ # Check if already running
90
+ if is_port_in_use(port):
91
+ pid = get_pid_on_port(port)
92
+ return {
93
+ "success": True,
94
+ "message": "Server already running",
95
+ "pid": pid,
96
+ "port": port,
97
+ "url": f"http://{host}:{port}",
98
+ "status": "already_running",
99
+ }
100
+
101
+ # Verify root_dir exists
102
+ if not os.path.isdir(root_dir):
103
+ return {"success": False, "error": f"Directory not found: {root_dir}"}
104
+
105
+ # Start Python HTTP server as background process
106
+ try:
107
+ process = subprocess.Popen(
108
+ [
109
+ "python3",
110
+ "-m",
111
+ "http.server",
112
+ str(port),
113
+ "--bind",
114
+ host,
115
+ "--directory",
116
+ root_dir,
117
+ ],
118
+ stdout=subprocess.DEVNULL,
119
+ stderr=subprocess.DEVNULL,
120
+ start_new_session=True,
121
+ )
122
+
123
+ # Save PID
124
+ with open(pid_file, "w") as f:
125
+ f.write(str(process.pid))
126
+
127
+ # Wait for server to start
128
+ time.sleep(STATIC_SERVER_START_WAIT_SECONDS)
129
+
130
+ if is_port_in_use(port):
131
+ return {
132
+ "success": True,
133
+ "message": "Server deployed successfully",
134
+ "pid": process.pid,
135
+ "port": port,
136
+ "url": f"http://{host}:{port}",
137
+ "root_dir": root_dir,
138
+ "status": "running",
139
+ }
140
+ else:
141
+ return {"success": False, "error": "Server started but not responding"}
142
+
143
+ except (OSError, subprocess.SubprocessError, ValueError) as e:
144
+ return {"success": False, "error": f"Failed to start server: {e}"}
145
+
146
+ elif action == "status":
147
+ is_running = is_port_in_use(port)
148
+ pid = get_pid_on_port(port)
149
+
150
+ saved_pid = None
151
+ if os.path.isfile(pid_file):
152
+ try:
153
+ with open(pid_file) as f:
154
+ saved_pid = int(f.read().strip())
155
+ except (OSError, ValueError):
156
+ pass
157
+
158
+ return {
159
+ "success": True,
160
+ "running": is_running,
161
+ "pid": pid,
162
+ "saved_pid": saved_pid,
163
+ "port": port,
164
+ "url": f"http://{host}:{port}" if is_running else None,
165
+ "status": "running" if is_running else "stopped",
166
+ }
167
+
168
+ elif action == "stop":
169
+ pid = get_pid_on_port(port)
170
+
171
+ if not pid:
172
+ if os.path.isfile(pid_file):
173
+ os.remove(pid_file)
174
+ return {
175
+ "success": True,
176
+ "message": "No server running on this port",
177
+ "port": port,
178
+ "status": "stopped",
179
+ }
180
+
181
+ try:
182
+ os.kill(pid, signal.SIGTERM)
183
+ time.sleep(PROCESS_TERMINATE_WAIT_SECONDS)
184
+
185
+ if is_port_in_use(port):
186
+ os.kill(pid, signal.SIGKILL)
187
+ time.sleep(PROCESS_TERMINATE_WAIT_SECONDS)
188
+
189
+ if os.path.isfile(pid_file):
190
+ os.remove(pid_file)
191
+
192
+ return {
193
+ "success": True,
194
+ "message": "Server stopped",
195
+ "pid": pid,
196
+ "port": port,
197
+ "status": "stopped",
198
+ }
199
+ except ProcessLookupError:
200
+ if os.path.isfile(pid_file):
201
+ os.remove(pid_file)
202
+ return {
203
+ "success": True,
204
+ "message": "Server was not running",
205
+ "port": port,
206
+ "status": "stopped",
207
+ }
208
+ except (OSError, subprocess.SubprocessError) as e:
209
+ return {"success": False, "error": f"Failed to stop server: {e}"}
210
+
211
+ else:
212
+ return {"success": False, "error": f"Unknown action: {action}"}
213
+
214
+
215
+ def op_websocket_server(impl: dict, target: MCard, ctx: dict) -> Any:
216
+ """
217
+ Execute websocket_server builtin - manages a Python-based WebSocket server.
218
+
219
+ Actions: deploy, status, stop
220
+ Config: port, host
221
+
222
+ Uses Python's asyncio-based WebSocket server internally.
223
+ """
224
+ import os
225
+ import signal
226
+ import socket
227
+ import subprocess
228
+ import sys
229
+ import time
230
+
231
+ def _resolve_config_value(val: Any) -> Any:
232
+ """Resolve environment variables in config values like '${VAR}'."""
233
+ if isinstance(val, str) and val.startswith("${") and val.endswith("}"):
234
+ env_var = val[2:-1]
235
+ return os.getenv(env_var, val)
236
+ return val
237
+
238
+ # Valid config keys: port, host, root_dir
239
+ config = impl.get("config", {})
240
+ action = ctx.get("action") or config.get("action", "status")
241
+
242
+ # Default to the canonical shared websocket port, with legacy fallback.
243
+ default_port = int(
244
+ os.getenv(ENV_WS_PORT, os.getenv(ENV_WS_PORT_LEGACY, str(DEFAULT_WS_PORT)))
245
+ )
246
+ raw_port = ctx.get("port") or config.get("port", default_port)
247
+ port = int(_resolve_config_value(raw_port))
248
+
249
+ host = ctx.get("host") or config.get("host", NETWORK_DEFAULT_HOST)
250
+
251
+ project_root = os.getcwd()
252
+ pid_file = os.path.join(project_root, f".websocket_server_{port}.pid")
253
+
254
+ def is_port_in_use(p: int) -> bool:
255
+ # Use lsof to check if port is in use (handles both IPv4 and IPv6)
256
+ try:
257
+ result = subprocess.run(
258
+ ["lsof", "-ti", f":{p}"], capture_output=True, text=True
259
+ )
260
+ return result.returncode == 0 and result.stdout.strip() != ""
261
+ except (OSError, subprocess.SubprocessError):
262
+ # Fallback to socket check
263
+ with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
264
+ return s.connect_ex(("localhost", p)) == 0
265
+
266
+ def get_pid_on_port(p: int):
267
+ try:
268
+ result = subprocess.run(
269
+ ["lsof", "-ti", f":{p}"], capture_output=True, text=True
270
+ )
271
+ if result.returncode == 0 and result.stdout.strip():
272
+ return int(result.stdout.strip().split("\n")[0])
273
+ except (OSError, subprocess.SubprocessError, ValueError):
274
+ pass
275
+ return None
276
+
277
+ if action == "deploy":
278
+ # Check if already running
279
+ if is_port_in_use(port):
280
+ pid = get_pid_on_port(port)
281
+ return {
282
+ "success": True,
283
+ "message": "WebSocket server already running",
284
+ "pid": pid,
285
+ "port": port,
286
+ "url": f"ws://{host}:{port}/",
287
+ "status": "already_running",
288
+ }
289
+
290
+ # Python WebSocket server script (inline)
291
+ ws_server_code = f'''
292
+ import asyncio
293
+ import json
294
+ import signal
295
+ import sys
296
+ import time
297
+ import yaml
298
+
299
+ try:
300
+ import websockets
301
+ except ImportError:
302
+ print("websockets module not found, using basic socket server")
303
+ sys.exit(1)
304
+
305
+ # Import PTR components for CLM execution
306
+ try:
307
+ from clm.layer0.loader import CLMChapterLoader
308
+ except ImportError:
309
+ print("Warning: clm not available, CLM execution disabled")
310
+ CLMChapterLoader = None
311
+
312
+ connected_clients = set()
313
+
314
+ async def execute_clm(clm_yaml, input_data):
315
+ """Execute CLM code and return result"""
316
+ if not CLMChapterLoader:
317
+ return {{"error": "CLM execution not available - clm not installed"}}
318
+
319
+ import tempfile
320
+ import os
321
+
322
+ try:
323
+ start_time = time.time()
324
+
325
+ # Parse YAML to get metadata
326
+ clm_data = yaml.safe_load(clm_yaml)
327
+
328
+ # Write YAML to temporary file
329
+ with tempfile.NamedTemporaryFile(mode='w', suffix='.yaml', delete=False) as f:
330
+ f.write(clm_yaml)
331
+ temp_path = f.name
332
+
333
+ try:
334
+ # Load chapter from YAML file
335
+ chapter = CLMChapterLoader.load_from_yaml(temp_path)
336
+
337
+ # Execute chapter's action directly with provided input
338
+ result, state, logs = chapter.action.execute(input_data, {{}})
339
+
340
+ execution_time = (time.time() - start_time) * 1000 # ms
341
+
342
+ return {{
343
+ "success": True,
344
+ "result": result,
345
+ "executionTime": execution_time,
346
+ "chapter": clm_data.get("chapter", {{}}).get("title", "Unknown"),
347
+ "concept": clm_data.get("clm", {{}}).get("abstract", {{}}).get("concept", "Unknown"),
348
+ "logs": logs
349
+ }}
350
+ finally:
351
+ # Clean up temp file
352
+ if os.path.exists(temp_path):
353
+ os.unlink(temp_path)
354
+
355
+ except (OSError, subprocess.SubprocessError, ValueError) as e:
356
+ return {{
357
+ "success": False,
358
+ "error": str(e),
359
+ "executionTime": 0
360
+ }}
361
+
362
+ async def handler(websocket):
363
+ connected_clients.add(websocket)
364
+ try:
365
+ async for message in websocket:
366
+ try:
367
+ data = json.loads(message)
368
+ msg_type = data.get("type")
369
+ message_id = data.get("messageId")
370
+
371
+ if msg_type == "clm_execute":
372
+ # Execute CLM
373
+ clm_code = data.get("clm", "")
374
+ input_data = data.get("input", {{}})
375
+
376
+ result = await execute_clm(clm_code, input_data)
377
+
378
+ # Send response with same messageId
379
+ response = {{
380
+ "messageId": message_id,
381
+ **result
382
+ }}
383
+ await websocket.send(json.dumps(response))
384
+
385
+ elif msg_type == "ping":
386
+ # Respond to ping
387
+ await websocket.send(json.dumps({{
388
+ "messageId": message_id,
389
+ "type": "pong",
390
+ "timestamp": data.get("timestamp")
391
+ }}))
392
+ else:
393
+ # Echo unknown messages
394
+ response = json.dumps({{
395
+ "messageId": message_id,
396
+ "type": "echo",
397
+ "original": message,
398
+ "clients": len(connected_clients)
399
+ }})
400
+ await websocket.send(response)
401
+
402
+ except json.JSONDecodeError:
403
+ await websocket.send(json.dumps({{
404
+ "error": "Invalid JSON"
405
+ }}))
406
+ except Exception as e:
407
+ await websocket.send(json.dumps({{
408
+ "error": str(e)
409
+ }}))
410
+
411
+ except websockets.exceptions.ConnectionClosed:
412
+ pass
413
+ finally:
414
+ connected_clients.discard(websocket)
415
+
416
+ async def main():
417
+ async with websockets.serve(handler, "{host}", {port}):
418
+ print("WebSocket CLM Execution Server running on ws://{host}:{port}/")
419
+ print("Ready to handle CLM execution requests")
420
+ await asyncio.Future() # Run forever
421
+
422
+ if __name__ == "__main__":
423
+ try:
424
+ asyncio.run(main())
425
+ except KeyboardInterrupt:
426
+ pass
427
+ '''
428
+
429
+ # Start Python WebSocket server as background process
430
+ try:
431
+ process = subprocess.Popen(
432
+ [sys.executable, "-c", ws_server_code],
433
+ stdout=subprocess.DEVNULL,
434
+ stderr=subprocess.DEVNULL,
435
+ start_new_session=True,
436
+ )
437
+
438
+ # Save PID
439
+ with open(pid_file, "w") as f:
440
+ f.write(str(process.pid))
441
+
442
+ # Wait for server to start
443
+ time.sleep(WEBSOCKET_SERVER_START_WAIT_SECONDS)
444
+
445
+ if is_port_in_use(port):
446
+ return {
447
+ "success": True,
448
+ "message": "WebSocket server deployed successfully",
449
+ "pid": process.pid,
450
+ "port": port,
451
+ "url": f"ws://{host}:{port}/",
452
+ "status": "running",
453
+ }
454
+ else:
455
+ return {
456
+ "success": False,
457
+ "error": "Server started but not responding. Ensure 'websockets' package is installed: pip install websockets",
458
+ }
459
+
460
+ except (OSError, subprocess.SubprocessError, ValueError) as e:
461
+ return {"success": False, "error": f"Failed to start server: {e}"}
462
+
463
+ elif action == "status":
464
+ is_running = is_port_in_use(port)
465
+ pid = get_pid_on_port(port)
466
+
467
+ saved_pid = None
468
+ if os.path.isfile(pid_file):
469
+ try:
470
+ with open(pid_file) as f:
471
+ saved_pid = int(f.read().strip())
472
+ except (OSError, ValueError):
473
+ pass
474
+
475
+ return {
476
+ "success": True,
477
+ "running": is_running,
478
+ "pid": pid,
479
+ "saved_pid": saved_pid,
480
+ "port": port,
481
+ "url": f"ws://{host}:{port}/" if is_running else None,
482
+ "status": "running" if is_running else "stopped",
483
+ }
484
+
485
+ elif action == "stop":
486
+ pid = get_pid_on_port(port)
487
+
488
+ if not pid:
489
+ if os.path.isfile(pid_file):
490
+ os.remove(pid_file)
491
+ return {
492
+ "success": True,
493
+ "message": "No WebSocket server running on this port",
494
+ "port": port,
495
+ "status": "stopped",
496
+ }
497
+
498
+ try:
499
+ os.kill(pid, signal.SIGTERM)
500
+ time.sleep(PROCESS_TERMINATE_WAIT_SECONDS)
501
+
502
+ if is_port_in_use(port):
503
+ os.kill(pid, signal.SIGKILL)
504
+ time.sleep(PROCESS_TERMINATE_WAIT_SECONDS)
505
+
506
+ if os.path.isfile(pid_file):
507
+ os.remove(pid_file)
508
+
509
+ return {
510
+ "success": True,
511
+ "message": "WebSocket server stopped",
512
+ "pid": pid,
513
+ "port": port,
514
+ "status": "stopped",
515
+ }
516
+ except ProcessLookupError:
517
+ if os.path.isfile(pid_file):
518
+ os.remove(pid_file)
519
+ return {
520
+ "success": True,
521
+ "message": "WebSocket server was not running",
522
+ "port": port,
523
+ "status": "stopped",
524
+ }
525
+ except (OSError, subprocess.SubprocessError) as e:
526
+ return {"success": False, "error": f"Failed to stop server: {e}"}
527
+
528
+ else:
529
+ return {"success": False, "error": f"Unknown action: {action}"}
@@ -0,0 +1,117 @@
1
+ """Baldwin Split Operator (DEL-388-02).
2
+
3
+ Decomposition Operator (÷): A -> A1 ⊗ A2.
4
+ Decomposes a compound MCard/PCard manifest into orthogonal components or
5
+ partitions input tokens across parallel branches, preserving triadic
6
+ syntactic closure (abstract_spec, concrete_impl, balanced_exp).
7
+ """
8
+ from __future__ import annotations
9
+
10
+ import copy
11
+ from typing import Any
12
+
13
+ import yaml
14
+
15
+ from clm.layer0.model.card import MCard
16
+
17
+
18
+ def _parse_manifest(target: MCard | str | dict[str, Any]) -> dict[str, Any]:
19
+ """Extract dict manifest from target MCard or string."""
20
+ if isinstance(target, dict):
21
+ return copy.deepcopy(target)
22
+ if isinstance(target, MCard):
23
+ raw = target.get_content(as_text=True)
24
+ else:
25
+ raw = str(target)
26
+ try:
27
+ data = yaml.safe_load(raw)
28
+ if isinstance(data, dict):
29
+ return data
30
+ except Exception:
31
+ pass
32
+ return {
33
+ "abstract_spec": {"type": "raw_content", "payload": raw},
34
+ "concrete_impl": {"runtime": "python", "length": len(raw)},
35
+ "balanced_exp": {"invariants": ["noetherian_conservation"]},
36
+ }
37
+
38
+
39
+ def ensure_triadic_closure(data: dict[str, Any], suffix: str = "") -> dict[str, Any]:
40
+ """Guarantee that data contains abstract_spec, concrete_impl, and balanced_exp."""
41
+ res = copy.deepcopy(data)
42
+ if "abstract_spec" not in res:
43
+ res["abstract_spec"] = res.get("abstract", {
44
+ "type": f"pcard/abstract_{suffix}" if suffix else "pcard/abstract",
45
+ "interface": "universal",
46
+ })
47
+ if "concrete_impl" not in res:
48
+ res["concrete_impl"] = res.get("concrete", {
49
+ "type": f"pcard/concrete_{suffix}" if suffix else "pcard/concrete",
50
+ "runtime": "python",
51
+ })
52
+ if "balanced_exp" not in res:
53
+ res["balanced_exp"] = res.get("balanced", {
54
+ "type": f"pcard/balanced_{suffix}" if suffix else "pcard/balanced",
55
+ "invariants": ["noetherian_conservation"],
56
+ })
57
+ return res
58
+
59
+
60
+ def op_split(impl: dict[str, Any], target: MCard, ctx: dict[str, Any]) -> dict[str, Any]:
61
+ """Execute the Baldwin Split (÷) operation.
62
+
63
+ Args:
64
+ impl: Configuration dict for split (e.g. split_keys, branch_a_keys, branch_b_keys).
65
+ target: Target MCard to decompose.
66
+ ctx: Execution context.
67
+
68
+ Returns:
69
+ Dict containing decomposed branches, manifests, and closure proofs.
70
+ """
71
+ manifest = _parse_manifest(target)
72
+ split_keys = impl.get("split_keys", ctx.get("split_keys", []))
73
+ branch_a_keys = impl.get("branch_a_keys", ctx.get("branch_a_keys", []))
74
+ branch_b_keys = impl.get("branch_b_keys", ctx.get("branch_b_keys", []))
75
+
76
+ if branch_a_keys or branch_b_keys:
77
+ part_a = {k: manifest[k] for k in branch_a_keys if k in manifest}
78
+ part_b = {k: manifest[k] for k in branch_b_keys if k in manifest}
79
+ elif split_keys:
80
+ part_a = {k: manifest[k] for k in split_keys if k in manifest}
81
+ part_b = {k: v for k, v in manifest.items() if k not in split_keys}
82
+ else:
83
+ # Default orthogonal partition
84
+ keys = list(manifest.keys())
85
+ mid = max(1, len(keys) // 2)
86
+ part_a = {k: manifest[k] for k in keys[:mid]}
87
+ part_b = {k: manifest[k] for k in keys[mid:]}
88
+
89
+ manifest_a = ensure_triadic_closure(part_a, "branch_a")
90
+ manifest_b = ensure_triadic_closure(part_b, "branch_b")
91
+
92
+ yaml_a = yaml.dump(manifest_a, default_flow_style=False)
93
+ yaml_b = yaml.dump(manifest_b, default_flow_style=False)
94
+
95
+ mcard_a = MCard(yaml_a)
96
+ mcard_b = MCard(yaml_b)
97
+
98
+ return {
99
+ "success": True,
100
+ "operator": "split",
101
+ "syntactic_closure": True,
102
+ "branch_a": {
103
+ "manifest": manifest_a,
104
+ "hash": mcard_a.hash,
105
+ "content": yaml_a,
106
+ },
107
+ "branch_b": {
108
+ "manifest": manifest_b,
109
+ "hash": mcard_b.hash,
110
+ "content": yaml_b,
111
+ },
112
+ "mcard_a": mcard_a,
113
+ "mcard_b": mcard_b,
114
+ }
115
+
116
+
117
+ __all__ = ["op_split", "ensure_triadic_closure"]