plan-manager-client 0.2.149__tar.gz → 0.2.151__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.
- {plan_manager_client-0.2.149 → plan_manager_client-0.2.151}/PKG-INFO +1 -1
- {plan_manager_client-0.2.149 → plan_manager_client-0.2.151}/plan_manager_client/client.py +5 -0
- {plan_manager_client-0.2.149 → plan_manager_client-0.2.151}/plan_manager_client/export_fetch_request.py +8 -2
- {plan_manager_client-0.2.149 → plan_manager_client-0.2.151}/plan_manager_client/facade_context_gate_mixin.py +4 -0
- plan_manager_client-0.2.151/plan_manager_client/facade_hrs_file_mixin.py +201 -0
- {plan_manager_client-0.2.149 → plan_manager_client-0.2.151}/plan_manager_client/facade_plan_hrs_mixin.py +8 -0
- plan_manager_client-0.2.151/plan_manager_client/facade_runtime_mixin/__init__.py +11 -0
- plan_manager_client-0.2.149/plan_manager_client/facade_runtime_mixin.py → plan_manager_client-0.2.151/plan_manager_client/facade_runtime_mixin/authoring_part.py +24 -190
- plan_manager_client-0.2.151/plan_manager_client/facade_runtime_mixin/facade.py +324 -0
- plan_manager_client-0.2.151/plan_manager_client/facade_runtime_mixin/overlay_part.py +192 -0
- {plan_manager_client-0.2.149 → plan_manager_client-0.2.151}/plan_manager_client/info_walker.py +22 -1
- {plan_manager_client-0.2.149 → plan_manager_client-0.2.151}/plan_manager_client/server_api.py +3 -0
- {plan_manager_client-0.2.149 → plan_manager_client-0.2.151}/plan_manager_client/server_api_mutating.py +1 -0
- {plan_manager_client-0.2.149 → plan_manager_client-0.2.151}/plan_manager_client.egg-info/PKG-INFO +1 -1
- {plan_manager_client-0.2.149 → plan_manager_client-0.2.151}/plan_manager_client.egg-info/SOURCES.txt +4 -1
- plan_manager_client-0.2.149/plan_manager_client/facade_hrs_file_mixin.py +0 -109
- {plan_manager_client-0.2.149 → plan_manager_client-0.2.151}/plan_manager_client/__init__.py +0 -0
- {plan_manager_client-0.2.149 → plan_manager_client-0.2.151}/plan_manager_client/_version.py +0 -0
- {plan_manager_client-0.2.149 → plan_manager_client-0.2.151}/plan_manager_client/authoring_diagnostics.py +0 -0
- {plan_manager_client-0.2.149 → plan_manager_client-0.2.151}/plan_manager_client/config.py +0 -0
- {plan_manager_client-0.2.149 → plan_manager_client-0.2.151}/plan_manager_client/delivery/__init__.py +0 -0
- {plan_manager_client-0.2.149 → plan_manager_client-0.2.151}/plan_manager_client/delivery/archive.py +0 -0
- {plan_manager_client-0.2.149 → plan_manager_client-0.2.151}/plan_manager_client/delivery/atomicity.py +0 -0
- {plan_manager_client-0.2.149 → plan_manager_client-0.2.151}/plan_manager_client/delivery/commit.py +0 -0
- {plan_manager_client-0.2.149 → plan_manager_client-0.2.151}/plan_manager_client/delivery/destination.py +0 -0
- {plan_manager_client-0.2.149 → plan_manager_client-0.2.151}/plan_manager_client/delivery/provenance.py +0 -0
- {plan_manager_client-0.2.149 → plan_manager_client-0.2.151}/plan_manager_client/delivery/upload.py +0 -0
- {plan_manager_client-0.2.149 → plan_manager_client-0.2.151}/plan_manager_client/dispatch.py +0 -0
- {plan_manager_client-0.2.149 → plan_manager_client-0.2.151}/plan_manager_client/export_fetch_chunks.py +0 -0
- {plan_manager_client-0.2.149 → plan_manager_client-0.2.151}/plan_manager_client/export_fetch_unpack.py +0 -0
- {plan_manager_client-0.2.149 → plan_manager_client-0.2.151}/plan_manager_client/export_fetch_verify.py +0 -0
- {plan_manager_client-0.2.149 → plan_manager_client-0.2.151}/plan_manager_client/facade_agent_config_mixin.py +0 -0
- {plan_manager_client-0.2.149 → plan_manager_client-0.2.151}/plan_manager_client/facade_bug_project_mixin.py +0 -0
- {plan_manager_client-0.2.149 → plan_manager_client-0.2.151}/plan_manager_client/facade_entity_common_mixin.py +0 -0
- {plan_manager_client-0.2.149 → plan_manager_client-0.2.151}/plan_manager_client/facade_step_graph_mixin.py +0 -0
- {plan_manager_client-0.2.149 → plan_manager_client-0.2.151}/plan_manager_client/scenario_runtime.py +0 -0
- {plan_manager_client-0.2.149 → plan_manager_client-0.2.151}/plan_manager_client.egg-info/dependency_links.txt +0 -0
- {plan_manager_client-0.2.149 → plan_manager_client-0.2.151}/plan_manager_client.egg-info/requires.txt +0 -0
- {plan_manager_client-0.2.149 → plan_manager_client-0.2.151}/plan_manager_client.egg-info/top_level.txt +0 -0
- {plan_manager_client-0.2.149 → plan_manager_client-0.2.151}/pyproject.toml +0 -0
- {plan_manager_client-0.2.149 → plan_manager_client-0.2.151}/setup.cfg +0 -0
- {plan_manager_client-0.2.149 → plan_manager_client-0.2.151}/tests/test_client_server_api_sync.py +0 -0
- {plan_manager_client-0.2.149 → plan_manager_client-0.2.151}/tests/test_contract_facade_passthrough.py +0 -0
- {plan_manager_client-0.2.149 → plan_manager_client-0.2.151}/tests/test_dispatch_validation_error_classification.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: plan-manager-client
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.151
|
|
4
4
|
Summary: First-class client library for the plan_manager JSON-RPC API: full command-surface facade plus export delivery module boundary.
|
|
5
5
|
Requires-Python: >=3.12
|
|
6
6
|
Requires-Dist: mcp-proxy-adapter==8.11.3
|
|
@@ -65,6 +65,11 @@ class PlanManagerClient(
|
|
|
65
65
|
canonical command methods.
|
|
66
66
|
"""
|
|
67
67
|
self._rpc = JsonRpcClient(**jsonrpc_kwargs)
|
|
68
|
+
# Chain the cooperative initialisers of every mixin in the MRO (for example
|
|
69
|
+
# RuntimeCommandsMixin, which creates runtimeOverlayPart and
|
|
70
|
+
# runtimeAuthoringPart). Called without arguments: the mixins take none, and
|
|
71
|
+
# object.__init__ rejects keyword arguments.
|
|
72
|
+
super().__init__()
|
|
68
73
|
|
|
69
74
|
|
|
70
75
|
__all__ = ["PlanManagerClient"]
|
|
@@ -18,7 +18,7 @@ class SupportsExportProduction(Protocol):
|
|
|
18
18
|
"""The two calls this module needs from a client facade."""
|
|
19
19
|
|
|
20
20
|
async def plan_export(
|
|
21
|
-
self, plan: str, revision: str | None = None
|
|
21
|
+
self, plan: str, revision: str | None = None, include_step_uuids: bool = False
|
|
22
22
|
) -> dict[str, Any]:
|
|
23
23
|
...
|
|
24
24
|
|
|
@@ -47,6 +47,7 @@ async def request_export_archive(
|
|
|
47
47
|
client: SupportsExportProduction,
|
|
48
48
|
plan: str,
|
|
49
49
|
revision: str | None = None,
|
|
50
|
+
include_step_uuids: bool = False,
|
|
50
51
|
) -> ExportArchiveRef:
|
|
51
52
|
"""Produce a plan's export tree and obtain the reference to its archive.
|
|
52
53
|
|
|
@@ -60,12 +61,17 @@ async def request_export_archive(
|
|
|
60
61
|
plan: The plan identifier whose export tree is produced and archived.
|
|
61
62
|
revision: Optional revision passed to the export production call only; when None
|
|
62
63
|
that call uses its own default revision selection.
|
|
64
|
+
include_step_uuids: When True the export descriptors carry step_uuid (bug 62cff4e4
|
|
65
|
+
part b); forwarded to plan_export only when True.
|
|
63
66
|
|
|
64
67
|
Returns:
|
|
65
68
|
The ExportArchiveRef naming exactly one archive file, with its size, declared
|
|
66
69
|
whole-file digest, and packed-file count.
|
|
67
70
|
"""
|
|
68
|
-
|
|
71
|
+
export_params: dict[str, Any] = {"plan": plan, "revision": revision}
|
|
72
|
+
if include_step_uuids:
|
|
73
|
+
export_params["include_step_uuids"] = True
|
|
74
|
+
await client.plan_export(**export_params)
|
|
69
75
|
response = await client.export_archive(plan=plan)
|
|
70
76
|
return ExportArchiveRef(
|
|
71
77
|
name=str(response["archive"]),
|
|
@@ -76,6 +76,10 @@ class ContextGateCommandsMixin:
|
|
|
76
76
|
"""Read one declared matrix namespace of a plan with its content_hash (parameters passed through; the server validates them)."""
|
|
77
77
|
return await self._call("matrix_namespace_get", params)
|
|
78
78
|
|
|
79
|
+
async def matrix_case_get(self, **params: Any) -> Any:
|
|
80
|
+
"""Read one matrix case of a plan by namespace and case_id; a case marked deleted is read with its mark (parameters passed through)."""
|
|
81
|
+
return await self._call("matrix_case_get", params)
|
|
82
|
+
|
|
79
83
|
async def matrix_lint(self, **params: Any) -> Any:
|
|
80
84
|
"""Lint the matrix parts of a plan and page the defects (parameters passed through)."""
|
|
81
85
|
return await self._call("matrix_lint", params)
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
"""HRS file-transfer client wrappers (import half): hrs_import_file.
|
|
2
|
+
|
|
3
|
+
Author: Vasiliy Zdanovskiy
|
|
4
|
+
email: vasilyvz@gmail.com
|
|
5
|
+
|
|
6
|
+
These two methods are client-side compositions of server commands, not
|
|
7
|
+
server commands themselves; they are exempted in
|
|
8
|
+
plan_manager_client.server_api.FILE_WRAPPER_NAMES.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
import hashlib
|
|
14
|
+
import os
|
|
15
|
+
import uuid
|
|
16
|
+
from pathlib import Path
|
|
17
|
+
from typing import Any, Callable
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _require_bare_filename(filename: str) -> None:
|
|
21
|
+
"""Refuse any filename that is not a single plain path component."""
|
|
22
|
+
if (
|
|
23
|
+
not filename
|
|
24
|
+
or filename in (".", "..")
|
|
25
|
+
or "/" in filename
|
|
26
|
+
or "\\" in filename
|
|
27
|
+
or os.path.basename(filename) != filename
|
|
28
|
+
):
|
|
29
|
+
raise ValueError(f"filename must be a bare file name, got {filename!r}")
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _require_uploadable_file(path: str) -> None:
|
|
33
|
+
"""Check the local source before any request: regular, readable and non-empty."""
|
|
34
|
+
with open(path, "rb"):
|
|
35
|
+
pass
|
|
36
|
+
if os.path.getsize(path) == 0:
|
|
37
|
+
raise ValueError(f"file is empty: {path}")
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def _reply_payload(reply: Any) -> Any:
|
|
41
|
+
"""Return the command outcome ({success, data|error}) of a reply.
|
|
42
|
+
|
|
43
|
+
The client's _call returns the queued-command job envelope
|
|
44
|
+
{job_id, command, result: {success, data}, status}; a bare outcome is
|
|
45
|
+
returned unchanged.
|
|
46
|
+
"""
|
|
47
|
+
if isinstance(reply, dict) and isinstance(reply.get("result"), dict) and "success" in reply["result"]:
|
|
48
|
+
return reply["result"]
|
|
49
|
+
return reply
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class HrsFileCommandsMixin:
|
|
53
|
+
"""Client-side wrappers that move a local file through the server transfer path.
|
|
54
|
+
|
|
55
|
+
Assumes it is mixed into a class that provides the held JSON-RPC client
|
|
56
|
+
as self._rpc (upload_file) and the coroutine
|
|
57
|
+
async def _call(self, command: str, params: dict[str, Any] | None = None) -> Any.
|
|
58
|
+
"""
|
|
59
|
+
|
|
60
|
+
async def hrs_import_file(
|
|
61
|
+
self,
|
|
62
|
+
plan: str,
|
|
63
|
+
path: str,
|
|
64
|
+
*,
|
|
65
|
+
dry_run: bool = True,
|
|
66
|
+
convert: bool = False,
|
|
67
|
+
filename: str | None = None,
|
|
68
|
+
compression: str = "identity",
|
|
69
|
+
chunk_size: int | None = None,
|
|
70
|
+
on_progress: Callable[[int, int], None] | None = None,
|
|
71
|
+
) -> Any:
|
|
72
|
+
"""Upload a local HRS file, then import it with hrs_import(source=filename).
|
|
73
|
+
|
|
74
|
+
Validation (bare filename, readable non-empty file) happens before any
|
|
75
|
+
request. The uploaded file's sha256 must match the transfer receipt
|
|
76
|
+
before hrs_import is called. A server error result is returned as is.
|
|
77
|
+
"""
|
|
78
|
+
if filename is None:
|
|
79
|
+
filename = f"hrs-import-{uuid.uuid4()}.md"
|
|
80
|
+
else:
|
|
81
|
+
_require_bare_filename(filename)
|
|
82
|
+
_require_uploadable_file(path)
|
|
83
|
+
|
|
84
|
+
receipt = await self._rpc.upload_file(
|
|
85
|
+
path,
|
|
86
|
+
filename=filename,
|
|
87
|
+
compression=compression,
|
|
88
|
+
chunk_size=chunk_size,
|
|
89
|
+
on_progress=on_progress,
|
|
90
|
+
)
|
|
91
|
+
saved = await self._call(
|
|
92
|
+
"export_upload_save",
|
|
93
|
+
{"transfer_id": receipt.transfer_id, "filename": filename},
|
|
94
|
+
)
|
|
95
|
+
payload = _reply_payload(saved)
|
|
96
|
+
if isinstance(payload, dict) and payload.get("success") is False:
|
|
97
|
+
return saved
|
|
98
|
+
body = payload.get("data") if isinstance(payload.get("data"), dict) else payload
|
|
99
|
+
if body.get("sha256") != receipt.checksum_value:
|
|
100
|
+
raise ValueError(
|
|
101
|
+
f"uploaded file checksum mismatch for {filename}: "
|
|
102
|
+
f"expected {receipt.checksum_value}, saved {body.get('sha256')}"
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
params: dict[str, Any] = {"plan": plan, "source": filename, "dry_run": dry_run}
|
|
106
|
+
if convert:
|
|
107
|
+
params["convert"] = True
|
|
108
|
+
return await self._call("hrs_import", params)
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
async def hrs_export_file(
|
|
112
|
+
self,
|
|
113
|
+
plan: str,
|
|
114
|
+
path: str,
|
|
115
|
+
*,
|
|
116
|
+
overwrite: bool = False,
|
|
117
|
+
compression: str = "identity",
|
|
118
|
+
chunk_size: int | None = None,
|
|
119
|
+
on_progress: Callable[[int, int], None] | None = None,
|
|
120
|
+
) -> Any:
|
|
121
|
+
"""Export a plan to a local file: hrs_export(target file), export_download_begin, download, sha256 check.
|
|
122
|
+
|
|
123
|
+
The export file stays on the server until the sweep timer removes it after its lifetime (no confirm
|
|
124
|
+
command). The download goes to a temporary file next to the destination; the destination is replaced
|
|
125
|
+
only by the final rename, after the sha256 of the downloaded bytes equals the export sha256. Without
|
|
126
|
+
overwrite an existing destination is refused after the verified download. A server error reply is
|
|
127
|
+
returned unchanged. A path that is empty or None is refused before any request.
|
|
128
|
+
"""
|
|
129
|
+
destination = _require_destination(path)
|
|
130
|
+
_require_writable_destination(destination, overwrite)
|
|
131
|
+
exported = _reply_payload(await self._call("hrs_export", {"plan": plan, "target": "file"}))
|
|
132
|
+
if not isinstance(exported, dict) or exported.get("success") is not True:
|
|
133
|
+
return exported
|
|
134
|
+
data = exported["data"]
|
|
135
|
+
begin_params: dict[str, Any] = {"plan": plan, "file": data["file"], "compression": compression}
|
|
136
|
+
if chunk_size is not None:
|
|
137
|
+
begin_params["chunk_size"] = chunk_size
|
|
138
|
+
begun_reply = await self._call("export_download_begin", begin_params)
|
|
139
|
+
begun = _reply_payload(begun_reply)
|
|
140
|
+
if not isinstance(begun, dict) or begun.get("success") is not True:
|
|
141
|
+
return begun_reply
|
|
142
|
+
transfer_id = begun["data"]["transfer_id"]
|
|
143
|
+
|
|
144
|
+
temporary = destination.with_name(f".{destination.name}.{uuid.uuid4().hex}.part")
|
|
145
|
+
try:
|
|
146
|
+
receipt = await self._rpc.download_file(
|
|
147
|
+
transfer_id, str(temporary), chunk_size=chunk_size, on_progress=on_progress
|
|
148
|
+
)
|
|
149
|
+
if not (receipt.completed and receipt.checksum_verified):
|
|
150
|
+
raise ValueError(f"download of {data['file']} is not completed or its checksum is not verified")
|
|
151
|
+
body = temporary.read_bytes()
|
|
152
|
+
if len(body) != data["size_bytes"] or hashlib.sha256(body).hexdigest() != data["sha256"]:
|
|
153
|
+
raise ValueError(f"downloaded file does not match the export {data['file']}")
|
|
154
|
+
if destination.exists() and not overwrite:
|
|
155
|
+
raise FileExistsError(f"destination exists and overwrite is False: {destination}")
|
|
156
|
+
os.replace(temporary, destination)
|
|
157
|
+
except BaseException:
|
|
158
|
+
with_suppressed_unlink(temporary)
|
|
159
|
+
raise
|
|
160
|
+
return {
|
|
161
|
+
"plan": plan,
|
|
162
|
+
"path": str(destination),
|
|
163
|
+
"size_bytes": data["size_bytes"],
|
|
164
|
+
"sha256": data["sha256"],
|
|
165
|
+
"paragraphs": data["paragraphs"],
|
|
166
|
+
"transfer_id": transfer_id,
|
|
167
|
+
"server_file": data["file"],
|
|
168
|
+
"expires_at": data["expires_at"],
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def _require_destination(path: Any) -> Path:
|
|
173
|
+
"""Return the destination path; an empty or missing path is refused before any request."""
|
|
174
|
+
if path is None or (isinstance(path, str) and not path.strip()):
|
|
175
|
+
raise ValueError("path must be a non-empty file path")
|
|
176
|
+
return Path(path)
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def _require_writable_destination(destination: Path, overwrite: bool) -> None:
|
|
180
|
+
"""Refuse a destination that cannot be written, before any request: a directory, a missing parent, a read-only
|
|
181
|
+
parent, or an existing file without overwrite."""
|
|
182
|
+
if destination.is_dir():
|
|
183
|
+
raise IsADirectoryError(f"destination is a directory: {destination}")
|
|
184
|
+
parent = destination.parent
|
|
185
|
+
if not parent.is_dir():
|
|
186
|
+
raise FileNotFoundError(f"destination directory does not exist: {parent}")
|
|
187
|
+
if not os.access(parent, os.W_OK | os.X_OK):
|
|
188
|
+
raise PermissionError(f"destination directory is not writable: {parent}")
|
|
189
|
+
if destination.exists() and not overwrite:
|
|
190
|
+
raise FileExistsError(f"destination exists and overwrite is False: {destination}")
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
def with_suppressed_unlink(path: Path) -> None:
|
|
194
|
+
"""Remove a temporary file if it exists."""
|
|
195
|
+
try:
|
|
196
|
+
path.unlink()
|
|
197
|
+
except FileNotFoundError:
|
|
198
|
+
pass
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
__all__ = ["HrsFileCommandsMixin"]
|
|
@@ -102,6 +102,10 @@ class PlanHrsCommandsMixin:
|
|
|
102
102
|
"""Export the byte-identical HRS Markdown text of a plan."""
|
|
103
103
|
return await self._call("hrs_export", params)
|
|
104
104
|
|
|
105
|
+
async def export_download_begin(self, **params: Any) -> Any:
|
|
106
|
+
"""Open the download session of one hrs_export file of a plan (plan, file, compression, chunk_size); the server file stays until the sweep timer removes it."""
|
|
107
|
+
return await self._call("export_download_begin", params)
|
|
108
|
+
|
|
105
109
|
async def export_cleanup(self, **params: Any) -> Any:
|
|
106
110
|
"""Purge export artifacts under the configured export root, classified per plan; dry_run defaults to true and reports what would be removed without removing it."""
|
|
107
111
|
return await self._call("export_cleanup", params)
|
|
@@ -138,6 +142,10 @@ class PlanHrsCommandsMixin:
|
|
|
138
142
|
"""Split one binding HRS paragraph, addressed by label, into several binding paragraphs cut at the given offsets."""
|
|
139
143
|
return await self._call("para_split", params)
|
|
140
144
|
|
|
145
|
+
async def hrs_fields_fill(self, **params: Any) -> Any:
|
|
146
|
+
"""Fill the kind, title and statement of broken HRS paragraphs with one model call each (queued; the body never changes)."""
|
|
147
|
+
return await self._call("hrs_fields_fill", params)
|
|
148
|
+
|
|
141
149
|
async def concept_get(self, **params: Any) -> Any:
|
|
142
150
|
"""Return one concept of a plan by its id."""
|
|
143
151
|
return await self._call("concept_get", params)
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"""Todo/runtime-link/comment and model-binding/execution/review/escalation command-family facade mixin.
|
|
2
|
+
|
|
3
|
+
Author: Vasiliy Zdanovskiy
|
|
4
|
+
email: vasilyvz@gmail.com
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from .authoring_part import RuntimeAuthoringPart
|
|
8
|
+
from .facade import RuntimeCommandsMixin
|
|
9
|
+
from .overlay_part import RuntimeOverlayPart
|
|
10
|
+
|
|
11
|
+
__all__ = ["RuntimeCommandsMixin"]
|
|
@@ -1,194 +1,31 @@
|
|
|
1
|
-
"""
|
|
2
|
-
|
|
3
|
-
Author: Vasiliy Zdanovskiy
|
|
4
|
-
email: vasilyvz@gmail.com
|
|
5
|
-
"""
|
|
1
|
+
"""Module for split package."""
|
|
6
2
|
|
|
7
3
|
from __future__ import annotations
|
|
8
|
-
|
|
9
4
|
from typing import Any
|
|
10
5
|
|
|
11
|
-
|
|
12
|
-
class RuntimeCommandsMixin:
|
|
6
|
+
class RuntimeAuthoringPart:
|
|
13
7
|
"""One method per plan_manager command in the runtime work-item family.
|
|
14
8
|
|
|
15
9
|
Assumes it is mixed into a class that also inherits
|
|
16
10
|
plan_manager_client.dispatch._CommandDispatchMixin, which supplies the
|
|
17
11
|
coroutine async def _call(self, command: str, params: dict[str, Any] | None = None) -> Any
|
|
18
|
-
used by every method below.
|
|
19
|
-
"""
|
|
20
|
-
|
|
21
|
-
async def authoring_usage_get(self, step: str, include_descendants: bool) -> Any:
|
|
22
|
-
"""Read persisted MACSC token usage for one planned step (its id, a uuid4) or its subtree."""
|
|
23
|
-
return await self._call("authoring_usage_get", {"step": step, "include_descendants": include_descendants})
|
|
24
|
-
|
|
25
|
-
async def todo_create(self, **params: Any) -> Any:
|
|
26
|
-
"""Create a new TODO work item with a primary anchor."""
|
|
27
|
-
return await self._call("todo_create", params)
|
|
28
|
-
|
|
29
|
-
async def todo_get(self, **params: Any) -> Any:
|
|
30
|
-
"""Fetch a single TODO work item by identifier."""
|
|
31
|
-
return await self._call("todo_get", params)
|
|
32
|
-
|
|
33
|
-
async def todo_list(self, **params: Any) -> Any:
|
|
34
|
-
"""List TODO work items with uniform filtering and pagination."""
|
|
35
|
-
return await self._call("todo_list", params)
|
|
36
|
-
|
|
37
|
-
async def todo_update(self, **params: Any) -> Any:
|
|
38
|
-
"""Update mutable fields of an existing TODO work item."""
|
|
39
|
-
return await self._call("todo_update", params)
|
|
40
|
-
|
|
41
|
-
async def todo_reanchor(self, **params: Any) -> Any:
|
|
42
|
-
"""Move a TODO item's primary anchor to a new target, with an audit record."""
|
|
43
|
-
return await self._call("todo_reanchor", params)
|
|
44
|
-
|
|
45
|
-
async def todo_resolve(self, **params: Any) -> Any:
|
|
46
|
-
"""Mark a TODO work item resolved."""
|
|
47
|
-
return await self._call("todo_resolve", params)
|
|
48
|
-
|
|
49
|
-
async def todo_close(self, **params: Any) -> Any:
|
|
50
|
-
"""Mark a TODO work item closed."""
|
|
51
|
-
return await self._call("todo_close", params)
|
|
52
|
-
|
|
53
|
-
async def todo_reopen(self, **params: Any) -> Any:
|
|
54
|
-
"""Reopen a TODO work item from a terminal state."""
|
|
55
|
-
return await self._call("todo_reopen", params)
|
|
56
|
-
|
|
57
|
-
async def todo_delete(self, **params: Any) -> Any:
|
|
58
|
-
"""Delete a TODO work item: soft by default, hard=true gated by the inbound-reference integrity check, dry_run previews."""
|
|
59
|
-
return await self._call("todo_delete", params)
|
|
60
|
-
|
|
61
|
-
async def todo_link_add(self, **params: Any) -> Any:
|
|
62
|
-
"""Create a typed link between two TODO work items."""
|
|
63
|
-
return await self._call("todo_link_add", params)
|
|
64
|
-
|
|
65
|
-
async def todo_link_remove(self, **params: Any) -> Any:
|
|
66
|
-
"""Remove (soft-delete) a typed link between two TODO work items."""
|
|
67
|
-
return await self._call("todo_link_remove", params)
|
|
68
|
-
|
|
69
|
-
async def todo_link_get(self, **params: Any) -> Any:
|
|
70
|
-
"""Read one todo link by its id (parameters passed through; the server validates them)."""
|
|
71
|
-
return await self._call("todo_link_get", params)
|
|
72
|
-
|
|
73
|
-
async def todo_link_list(self, **params: Any) -> Any:
|
|
74
|
-
"""List todo links with filters and pagination (parameters passed through; the server validates them)."""
|
|
75
|
-
return await self._call("todo_link_list", params)
|
|
76
|
-
|
|
77
|
-
async def todo_link_delete(self, **params: Any) -> Any:
|
|
78
|
-
"""Delete a todo link: mark by default, hard=true removes a marked link, dry_run previews (parameters passed through; the server validates them)."""
|
|
79
|
-
return await self._call("todo_link_delete", params)
|
|
80
|
-
|
|
81
|
-
async def todo_queue(self, **params: Any) -> Any:
|
|
82
|
-
"""Surface a paginated page of the TODO-derived portion of the unified runtime work queue."""
|
|
83
|
-
return await self._call("todo_queue", params)
|
|
84
|
-
|
|
85
|
-
async def wish_create(self, **params: Any) -> Any:
|
|
86
|
-
"""Create a new runtime wish item."""
|
|
87
|
-
return await self._call("wish_create", params)
|
|
88
|
-
|
|
89
|
-
async def wish_get(self, **params: Any) -> Any:
|
|
90
|
-
"""Fetch a single runtime wish item by identifier."""
|
|
91
|
-
return await self._call("wish_get", params)
|
|
92
|
-
|
|
93
|
-
async def wish_list(self, **params: Any) -> Any:
|
|
94
|
-
"""List runtime wish items with filtering and pagination."""
|
|
95
|
-
return await self._call("wish_list", params)
|
|
96
|
-
|
|
97
|
-
async def wish_update(self, **params: Any) -> Any:
|
|
98
|
-
"""Update mutable fields of a runtime wish item."""
|
|
99
|
-
return await self._call("wish_update", params)
|
|
100
|
-
|
|
101
|
-
async def wish_reanchor(self, **params: Any) -> Any:
|
|
102
|
-
"""Move a wish item's primary anchor to a new target, with an audit record."""
|
|
103
|
-
return await self._call("wish_reanchor", params)
|
|
12
|
+
used by every method below."""
|
|
104
13
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
return await self._call("wish_delete", params)
|
|
14
|
+
def __init__(self, owner):
|
|
15
|
+
self._owner = owner
|
|
108
16
|
|
|
109
|
-
|
|
110
|
-
"""
|
|
111
|
-
|
|
17
|
+
def __getattr__(self, name):
|
|
18
|
+
"""Resolve names this part does not define on the owning facade."""
|
|
19
|
+
if name == "_owner":
|
|
20
|
+
raise AttributeError(name)
|
|
21
|
+
return getattr(self._owner, name)
|
|
112
22
|
|
|
113
|
-
async def
|
|
114
|
-
"""
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
return await self._call("calendar_entry_list", params)
|
|
120
|
-
|
|
121
|
-
async def calendar_entry_update(self, **params: Any) -> Any:
|
|
122
|
-
"""Update mutable fields of a calendar-based work entry."""
|
|
123
|
-
return await self._call("calendar_entry_update", params)
|
|
124
|
-
|
|
125
|
-
async def calendar_entry_reanchor(self, **params: Any) -> Any:
|
|
126
|
-
"""Move a calendar entry's primary anchor to a new target, with an audit record."""
|
|
127
|
-
return await self._call("calendar_entry_reanchor", params)
|
|
128
|
-
|
|
129
|
-
async def calendar_entry_delete(self, **params: Any) -> Any:
|
|
130
|
-
"""Delete a calendar-based work entry: soft by default, hard=true available, dry_run previews."""
|
|
131
|
-
return await self._call("calendar_entry_delete", params)
|
|
132
|
-
|
|
133
|
-
async def runtime_link_add(self, **params: Any) -> Any:
|
|
134
|
-
"""Create a typed link between two runtime records, each independently a bug or a todo."""
|
|
135
|
-
return await self._call("runtime_link_add", params)
|
|
136
|
-
|
|
137
|
-
async def runtime_link_list(self, **params: Any) -> Any:
|
|
138
|
-
"""List generic runtime links, optionally filtered to one endpoint record, with uniform pagination."""
|
|
139
|
-
return await self._call("runtime_link_list", params)
|
|
140
|
-
|
|
141
|
-
async def runtime_link_remove(self, **params: Any) -> Any:
|
|
142
|
-
"""Remove (soft-delete) a generic runtime link, with an optional dry-run preview."""
|
|
143
|
-
return await self._call("runtime_link_remove", params)
|
|
144
|
-
|
|
145
|
-
async def runtime_link_get(self, **params: Any) -> Any:
|
|
146
|
-
"""Read one runtime link by its id (parameters passed through; the server validates them)."""
|
|
147
|
-
return await self._call("runtime_link_get", params)
|
|
148
|
-
|
|
149
|
-
async def runtime_link_delete(self, **params: Any) -> Any:
|
|
150
|
-
"""Delete a runtime link: mark by default, hard=true removes a marked link, dry_run previews (parameters passed through; the server validates them)."""
|
|
151
|
-
return await self._call("runtime_link_delete", params)
|
|
152
|
-
|
|
153
|
-
async def runtime_overlay_export(self, **params: Any) -> Any:
|
|
154
|
-
"""Export the runtime overlay of one plan to overlay.json in the plan export folder (parameters passed through)."""
|
|
155
|
-
return await self._call("runtime_overlay_export", params)
|
|
156
|
-
|
|
157
|
-
async def runtime_overlay_import(self, **params: Any) -> Any:
|
|
158
|
-
"""Import a runtime overlay file into its plan, all or nothing; dry_run validates and counts only (parameters passed through)."""
|
|
159
|
-
return await self._call("runtime_overlay_import", params)
|
|
160
|
-
|
|
161
|
-
async def comment_add(self, **params: Any) -> Any:
|
|
162
|
-
"""Create a new runtime comment attached to a comment anchor."""
|
|
163
|
-
return await self._call("comment_add", params)
|
|
164
|
-
|
|
165
|
-
async def comment_get(self, **params: Any) -> Any:
|
|
166
|
-
"""Retrieve a single runtime comment by identifier."""
|
|
167
|
-
return await self._call("comment_get", params)
|
|
168
|
-
|
|
169
|
-
async def comment_list(self, **params: Any) -> Any:
|
|
170
|
-
"""List runtime comments with uniform filtering and pagination."""
|
|
171
|
-
return await self._call("comment_list", params)
|
|
172
|
-
|
|
173
|
-
async def comment_supersede(self, **params: Any) -> Any:
|
|
174
|
-
"""Create a new runtime comment that supersedes an existing one, preserving history."""
|
|
175
|
-
return await self._call("comment_supersede", params)
|
|
176
|
-
|
|
177
|
-
async def comment_resolve(self, **params: Any) -> Any:
|
|
178
|
-
"""Mark an existing runtime comment as resolved."""
|
|
179
|
-
return await self._call("comment_resolve", params)
|
|
180
|
-
|
|
181
|
-
async def comment_update(self, **params: Any) -> Any:
|
|
182
|
-
"""Change the body, kind, visibility or author of a runtime comment in place (parameters passed through; the server validates them)."""
|
|
183
|
-
return await self._call("comment_update", params)
|
|
184
|
-
|
|
185
|
-
async def comment_reanchor(self, **params: Any) -> Any:
|
|
186
|
-
"""Move a runtime comment's primary anchor to a new target, with an audit record."""
|
|
187
|
-
return await self._call("comment_reanchor", params)
|
|
188
|
-
|
|
189
|
-
async def comment_delete(self, **params: Any) -> Any:
|
|
190
|
-
"""Delete a runtime comment: soft by default, hard=true gated by the inbound-reference integrity check, dry_run previews."""
|
|
191
|
-
return await self._call("comment_delete", params)
|
|
23
|
+
async def authoring_usage_get(self, step: str, include_descendants: bool, plan: str | None = None) -> Any:
|
|
24
|
+
"""Read persisted MACSC token usage for one planned step (its id, a uuid4, or with plan its path) or its subtree."""
|
|
25
|
+
params: dict[str, Any] = {"step": step, "include_descendants": include_descendants}
|
|
26
|
+
if plan is not None:
|
|
27
|
+
params["plan"] = plan
|
|
28
|
+
return await self._call("authoring_usage_get", params)
|
|
192
29
|
|
|
193
30
|
async def model_binding_set(self, **params: Any) -> Any:
|
|
194
31
|
"""Create a model binding runtime-configuration record (C-009) for the given scope."""
|
|
@@ -273,9 +110,7 @@ class RuntimeCommandsMixin:
|
|
|
273
110
|
params["orchestrator_model_id"] = orchestrator_model_id
|
|
274
111
|
return await self._call("authoring_job_resume", params)
|
|
275
112
|
|
|
276
|
-
async def authoring_job_stop(
|
|
277
|
-
self, plan: str, step: str, session_id: str
|
|
278
|
-
) -> Any:
|
|
113
|
+
async def authoring_job_stop(self, plan: str, step: str, session_id: str) -> Any:
|
|
279
114
|
"""Stop the authoring job of the step ``step`` (its id, a uuid4) and its active same-session descendants."""
|
|
280
115
|
return await self._call(
|
|
281
116
|
"authoring_job_stop",
|
|
@@ -286,9 +121,12 @@ class RuntimeCommandsMixin:
|
|
|
286
121
|
"""Delete transient context for one terminal authoring job."""
|
|
287
122
|
return await self._call("authoring_job_delete", {"job_id": job_id})
|
|
288
123
|
|
|
289
|
-
async def authoring_child_list(self, step: str) -> Any:
|
|
290
|
-
"""List the direct planned children of the step ``step`` (its id, a uuid4)."""
|
|
291
|
-
|
|
124
|
+
async def authoring_child_list(self, step: str, plan: str | None = None) -> Any:
|
|
125
|
+
"""List the direct planned children of the step ``step`` (its id, a uuid4, or with ``plan`` its path)."""
|
|
126
|
+
params: dict[str, Any] = {"step": step}
|
|
127
|
+
if plan is not None:
|
|
128
|
+
params["plan"] = plan
|
|
129
|
+
return await self._call("authoring_child_list", params)
|
|
292
130
|
|
|
293
131
|
async def authoring_step_create(self, **params: Any) -> Any:
|
|
294
132
|
return await self._call("authoring_step_create", params)
|
|
@@ -301,9 +139,7 @@ class RuntimeCommandsMixin:
|
|
|
301
139
|
|
|
302
140
|
async def authoring_step_submit(self, step_payload: dict[str, Any]) -> Any:
|
|
303
141
|
"""Submit the current job's assigned-step payload."""
|
|
304
|
-
return await self._call(
|
|
305
|
-
"authoring_step_submit", {"step_payload": step_payload}
|
|
306
|
-
)
|
|
142
|
+
return await self._call("authoring_step_submit", {"step_payload": step_payload})
|
|
307
143
|
|
|
308
144
|
async def authoring_escalate(self, question: str) -> Any:
|
|
309
145
|
"""Pause the current job with a question for its direct parent."""
|
|
@@ -425,5 +261,3 @@ class RuntimeCommandsMixin:
|
|
|
425
261
|
"""Delete an answer envelope: soft by default, hard when hard=true, dry_run previews."""
|
|
426
262
|
return await self._call("answer_envelope_delete", params)
|
|
427
263
|
|
|
428
|
-
|
|
429
|
-
__all__ = ["RuntimeCommandsMixin"]
|