roborean-documents-base 0.1.2__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.
- roborean_documents_base/__init__.py +24 -0
- roborean_documents_base/artifact.py +15 -0
- roborean_documents_base/capabilities.py +30 -0
- roborean_documents_base/errors.py +13 -0
- roborean_documents_base/protocol.py +64 -0
- roborean_documents_base/registry.py +69 -0
- roborean_documents_base/resolve_values.py +27 -0
- roborean_documents_base/session.py +161 -0
- roborean_documents_base/template_store.py +56 -0
- roborean_documents_base-0.1.2.dist-info/METADATA +19 -0
- roborean_documents_base-0.1.2.dist-info/RECORD +12 -0
- roborean_documents_base-0.1.2.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"""Document driver protocols and session manager for Roborean."""
|
|
2
|
+
|
|
3
|
+
from .artifact import hash_bytes, write_artifact
|
|
4
|
+
from .capabilities import CapabilitySet, assert_op_allowed
|
|
5
|
+
from .errors import DriverError, TemplateError, UnsupportedOperationError
|
|
6
|
+
from .registry import DriverRegistry, load_entry_point_drivers
|
|
7
|
+
from .session import DocumentSessionManager
|
|
8
|
+
from .template_store import DocumentTemplateStore
|
|
9
|
+
|
|
10
|
+
__version__ = "0.3.0"
|
|
11
|
+
|
|
12
|
+
__all__ = [
|
|
13
|
+
"CapabilitySet",
|
|
14
|
+
"DocumentSessionManager",
|
|
15
|
+
"DocumentTemplateStore",
|
|
16
|
+
"DriverError",
|
|
17
|
+
"DriverRegistry",
|
|
18
|
+
"TemplateError",
|
|
19
|
+
"UnsupportedOperationError",
|
|
20
|
+
"assert_op_allowed",
|
|
21
|
+
"hash_bytes",
|
|
22
|
+
"load_entry_point_drivers",
|
|
23
|
+
"write_artifact",
|
|
24
|
+
]
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"""Artifact hashing helpers."""
|
|
2
|
+
|
|
3
|
+
import hashlib
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def hash_bytes(data: bytes) -> str:
|
|
8
|
+
"""Return a lowercase SHA-256 hex digest."""
|
|
9
|
+
return hashlib.sha256(data).hexdigest()
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def write_artifact(path: Path, data: bytes) -> None:
|
|
13
|
+
"""Write artifact bytes, creating parent directories."""
|
|
14
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
15
|
+
path.write_bytes(data)
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"""Capability enforcement for document operations."""
|
|
2
|
+
|
|
3
|
+
from collections.abc import Iterable
|
|
4
|
+
|
|
5
|
+
from roborean_spec import DocumentDriverManifest, DocumentOperation
|
|
6
|
+
|
|
7
|
+
from .errors import UnsupportedOperationError
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class CapabilitySet:
|
|
11
|
+
"""Immutable set of operation capability identifiers."""
|
|
12
|
+
|
|
13
|
+
def __init__(self, capabilities: Iterable[str]) -> None:
|
|
14
|
+
"""Store capabilities."""
|
|
15
|
+
self._items = frozenset(capabilities)
|
|
16
|
+
|
|
17
|
+
def allows(self, op_name: str) -> bool:
|
|
18
|
+
"""Return whether ``op_name`` is advertised."""
|
|
19
|
+
return op_name in self._items
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def assert_op_allowed(
|
|
23
|
+
manifest: DocumentDriverManifest,
|
|
24
|
+
op: DocumentOperation,
|
|
25
|
+
) -> None:
|
|
26
|
+
"""Raise when an operation is outside the driver capability set."""
|
|
27
|
+
if op.op not in manifest.capabilities:
|
|
28
|
+
raise UnsupportedOperationError(
|
|
29
|
+
f"Driver {manifest.driver_id} does not support {op.op}"
|
|
30
|
+
)
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"""Document driver and template errors."""
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class DriverError(Exception):
|
|
5
|
+
"""Base document driver failure."""
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class UnsupportedOperationError(DriverError):
|
|
9
|
+
"""Operation not in the driver's capability set."""
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class TemplateError(DriverError):
|
|
13
|
+
"""Template missing or invalid."""
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"""Document driver and session protocols."""
|
|
2
|
+
|
|
3
|
+
from typing import Any, Mapping, Protocol
|
|
4
|
+
|
|
5
|
+
from roborean_spec import (
|
|
6
|
+
DocumentDriverManifest,
|
|
7
|
+
DocumentOperation,
|
|
8
|
+
DocumentPreview,
|
|
9
|
+
TemplateManifest,
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
from .template_store import DocumentTemplateStore
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class DocumentSession(Protocol):
|
|
16
|
+
"""Mutable per-document generation state."""
|
|
17
|
+
|
|
18
|
+
document_id: str
|
|
19
|
+
driver_id: str
|
|
20
|
+
ops_applied: list[dict[str, Any]]
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class DocumentDriver(Protocol):
|
|
24
|
+
"""Format-specific document engine.
|
|
25
|
+
|
|
26
|
+
Attributes:
|
|
27
|
+
driver_id: Stable id such as ``roborean.docx``.
|
|
28
|
+
manifest: Driver capability manifest.
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
driver_id: str
|
|
32
|
+
manifest: DocumentDriverManifest
|
|
33
|
+
|
|
34
|
+
def load_template(
|
|
35
|
+
self,
|
|
36
|
+
template_ref: str,
|
|
37
|
+
*,
|
|
38
|
+
store: DocumentTemplateStore,
|
|
39
|
+
manifest: TemplateManifest,
|
|
40
|
+
) -> None:
|
|
41
|
+
"""Load mandatory template bytes into driver-internal state."""
|
|
42
|
+
|
|
43
|
+
def begin_session(
|
|
44
|
+
self,
|
|
45
|
+
workspace: Any,
|
|
46
|
+
metadata: Mapping[str, Any],
|
|
47
|
+
) -> DocumentSession:
|
|
48
|
+
"""Open a session bound to current workspace snapshot."""
|
|
49
|
+
|
|
50
|
+
def apply_operation(
|
|
51
|
+
self,
|
|
52
|
+
session: DocumentSession,
|
|
53
|
+
op: DocumentOperation,
|
|
54
|
+
) -> None:
|
|
55
|
+
"""Apply one typed operation."""
|
|
56
|
+
|
|
57
|
+
def finalize(self, session: DocumentSession) -> None:
|
|
58
|
+
"""Seal the session before serialization."""
|
|
59
|
+
|
|
60
|
+
def serialize(self, session: DocumentSession) -> bytes:
|
|
61
|
+
"""Return artifact bytes."""
|
|
62
|
+
|
|
63
|
+
def preview(self, session: DocumentSession) -> DocumentPreview | None:
|
|
64
|
+
"""Optional backend preview."""
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"""Discover installed document drivers."""
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
import os
|
|
5
|
+
from importlib.metadata import entry_points
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
from roborean_spec import DocumentDriverManifest
|
|
9
|
+
|
|
10
|
+
logger = logging.getLogger(__name__)
|
|
11
|
+
|
|
12
|
+
DOCUMENT_DRIVERS_GROUP = "roborean.document_drivers"
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class DriverRegistry:
|
|
16
|
+
"""Maps driver ids to driver instances or factories."""
|
|
17
|
+
|
|
18
|
+
def __init__(self) -> None:
|
|
19
|
+
"""Create an empty registry."""
|
|
20
|
+
self._drivers: dict[str, Any] = {}
|
|
21
|
+
|
|
22
|
+
def register(self, driver: Any) -> None:
|
|
23
|
+
"""Register a driver instance exposing ``driver_id``."""
|
|
24
|
+
self._drivers[driver.driver_id] = driver
|
|
25
|
+
|
|
26
|
+
def get(self, driver_id: str) -> Any:
|
|
27
|
+
"""Return a registered driver."""
|
|
28
|
+
return self._drivers[driver_id]
|
|
29
|
+
|
|
30
|
+
def list_ids(self) -> list[str]:
|
|
31
|
+
"""Return registered driver ids."""
|
|
32
|
+
return sorted(self._drivers)
|
|
33
|
+
|
|
34
|
+
def manifests(self) -> dict[str, DocumentDriverManifest]:
|
|
35
|
+
"""Return driver manifests keyed by id."""
|
|
36
|
+
return {
|
|
37
|
+
driver_id: driver.manifest
|
|
38
|
+
for driver_id, driver in self._drivers.items()
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def load_entry_point_drivers(
|
|
43
|
+
registry: DriverRegistry | None = None,
|
|
44
|
+
) -> DriverRegistry:
|
|
45
|
+
"""Load ``roborean.document_drivers`` entry points into a registry."""
|
|
46
|
+
registry = registry or DriverRegistry()
|
|
47
|
+
selected = entry_points().select(group=DOCUMENT_DRIVERS_GROUP)
|
|
48
|
+
allow_third = os.environ.get("ROBOREAN_ALLOW_THIRD_PARTY_PLUGINS", "") in {
|
|
49
|
+
"1",
|
|
50
|
+
"true",
|
|
51
|
+
"TRUE",
|
|
52
|
+
}
|
|
53
|
+
for item in selected:
|
|
54
|
+
try:
|
|
55
|
+
factory = item.load()
|
|
56
|
+
driver = factory() if callable(factory) else factory
|
|
57
|
+
# Built-in packages are always trusted; third-party optional.
|
|
58
|
+
trust = getattr(driver, "trust_tier", "core")
|
|
59
|
+
if trust == "third_party" and not allow_third:
|
|
60
|
+
logger.debug("Skipping third-party driver %s", item.name)
|
|
61
|
+
continue
|
|
62
|
+
registry.register(driver)
|
|
63
|
+
except Exception:
|
|
64
|
+
logger.debug(
|
|
65
|
+
"Failed loading document driver %s",
|
|
66
|
+
item.name,
|
|
67
|
+
exc_info=True,
|
|
68
|
+
)
|
|
69
|
+
return registry
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"""Resolve workspace values for document ops."""
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
from roborean_spec import PublicLiteral, SecretRefValue, WorkspaceValue
|
|
6
|
+
|
|
7
|
+
from .errors import DriverError
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def public_literal_value(value: WorkspaceValue) -> Any:
|
|
11
|
+
"""Extract a public literal payload; reject secrets for previews."""
|
|
12
|
+
if isinstance(value, PublicLiteral):
|
|
13
|
+
return value.value
|
|
14
|
+
if isinstance(value, SecretRefValue):
|
|
15
|
+
raise DriverError(
|
|
16
|
+
"secret_ref values cannot be rendered into document previews"
|
|
17
|
+
)
|
|
18
|
+
raise DriverError(f"Unsupported workspace value kind: {value.kind}")
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def display_value(value: WorkspaceValue) -> str:
|
|
22
|
+
"""Return a redacted display string for previews."""
|
|
23
|
+
if isinstance(value, PublicLiteral):
|
|
24
|
+
return str(value.value)
|
|
25
|
+
if isinstance(value, SecretRefValue):
|
|
26
|
+
return value.display_hint or "***"
|
|
27
|
+
return "***"
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
"""Orchestrate document sessions for a compiled project run."""
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass, field
|
|
4
|
+
from datetime import UTC, datetime
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
from roborean_spec import (
|
|
8
|
+
ArtifactRecord,
|
|
9
|
+
CompiledProject,
|
|
10
|
+
DocumentOperation,
|
|
11
|
+
DocumentPreview,
|
|
12
|
+
Project,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
from .artifact import hash_bytes
|
|
16
|
+
from .capabilities import assert_op_allowed
|
|
17
|
+
from .errors import DriverError, TemplateError
|
|
18
|
+
from .registry import DriverRegistry
|
|
19
|
+
from .template_store import DocumentTemplateStore
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@dataclass
|
|
23
|
+
class _OpenSession:
|
|
24
|
+
"""Internal bookkeeping for one open document session."""
|
|
25
|
+
|
|
26
|
+
document_id: str
|
|
27
|
+
driver: Any
|
|
28
|
+
session: Any
|
|
29
|
+
template_id: str
|
|
30
|
+
template_version: str
|
|
31
|
+
output_target: str
|
|
32
|
+
media_type: str
|
|
33
|
+
payload: bytes | None = None
|
|
34
|
+
preview: DocumentPreview | None = None
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@dataclass
|
|
38
|
+
class DocumentSessionManager:
|
|
39
|
+
"""Open sessions, apply ops, and finalize artifacts."""
|
|
40
|
+
|
|
41
|
+
registry: DriverRegistry
|
|
42
|
+
store: DocumentTemplateStore
|
|
43
|
+
_sessions: dict[str, _OpenSession] = field(default_factory=dict)
|
|
44
|
+
|
|
45
|
+
def open_all(
|
|
46
|
+
self,
|
|
47
|
+
project: Project,
|
|
48
|
+
compiled: CompiledProject,
|
|
49
|
+
workspace: Any,
|
|
50
|
+
) -> None:
|
|
51
|
+
"""Load templates and begin sessions for every document definition."""
|
|
52
|
+
self._sessions.clear()
|
|
53
|
+
for definition in project.documents:
|
|
54
|
+
driver = self.registry.get(definition.driver)
|
|
55
|
+
manifest = self.store.load_manifest(
|
|
56
|
+
definition.template_ref,
|
|
57
|
+
manifest_ref=definition.template_manifest_ref,
|
|
58
|
+
)
|
|
59
|
+
if manifest.driver != definition.driver:
|
|
60
|
+
raise TemplateError(
|
|
61
|
+
f"Manifest driver {manifest.driver} != {definition.driver}"
|
|
62
|
+
)
|
|
63
|
+
driver.load_template(
|
|
64
|
+
definition.template_ref,
|
|
65
|
+
store=self.store,
|
|
66
|
+
manifest=manifest,
|
|
67
|
+
)
|
|
68
|
+
session = driver.begin_session(
|
|
69
|
+
workspace,
|
|
70
|
+
{
|
|
71
|
+
"documentId": definition.id,
|
|
72
|
+
"projectId": project.id,
|
|
73
|
+
"settings": definition.settings,
|
|
74
|
+
},
|
|
75
|
+
)
|
|
76
|
+
media = (
|
|
77
|
+
driver.manifest.template_media_types[0]
|
|
78
|
+
if driver.manifest.template_media_types
|
|
79
|
+
else "application/octet-stream"
|
|
80
|
+
)
|
|
81
|
+
self._sessions[definition.id] = _OpenSession(
|
|
82
|
+
document_id=definition.id,
|
|
83
|
+
driver=driver,
|
|
84
|
+
session=session,
|
|
85
|
+
template_id=manifest.template_id,
|
|
86
|
+
template_version=manifest.template_version,
|
|
87
|
+
output_target=definition.output_target
|
|
88
|
+
or f"{definition.id}.bin",
|
|
89
|
+
media_type=media,
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
def apply(self, op: DocumentOperation | dict[str, Any]) -> None:
|
|
93
|
+
"""Route one operation to the correct session."""
|
|
94
|
+
if isinstance(op, dict):
|
|
95
|
+
operation = DocumentOperation.model_validate(op)
|
|
96
|
+
else:
|
|
97
|
+
operation = op
|
|
98
|
+
opened = self._sessions.get(operation.document_id)
|
|
99
|
+
if opened is None:
|
|
100
|
+
raise DriverError(
|
|
101
|
+
f"No open session for document {operation.document_id}"
|
|
102
|
+
)
|
|
103
|
+
assert_op_allowed(opened.driver.manifest, operation)
|
|
104
|
+
opened.driver.apply_operation(opened.session, operation)
|
|
105
|
+
|
|
106
|
+
def finalize_all(self) -> list[ArtifactRecord]:
|
|
107
|
+
"""Finalize, serialize, hash, and return artifact records."""
|
|
108
|
+
records: list[ArtifactRecord] = []
|
|
109
|
+
for opened in self._sessions.values():
|
|
110
|
+
opened.driver.finalize(opened.session)
|
|
111
|
+
payload = opened.driver.serialize(opened.session)
|
|
112
|
+
opened.payload = payload
|
|
113
|
+
opened.preview = opened.driver.preview(opened.session)
|
|
114
|
+
records.append(
|
|
115
|
+
ArtifactRecord(
|
|
116
|
+
documentId=opened.document_id,
|
|
117
|
+
path=opened.output_target,
|
|
118
|
+
mediaType=opened.media_type,
|
|
119
|
+
digestSha256=hash_bytes(payload),
|
|
120
|
+
byteLength=len(payload),
|
|
121
|
+
templateId=opened.template_id,
|
|
122
|
+
templateVersion=opened.template_version,
|
|
123
|
+
driverId=opened.driver.driver_id,
|
|
124
|
+
driverVersion=opened.driver.manifest.version,
|
|
125
|
+
)
|
|
126
|
+
)
|
|
127
|
+
return records
|
|
128
|
+
|
|
129
|
+
def payloads(self) -> dict[str, bytes]:
|
|
130
|
+
"""Return serialized payloads keyed by document id."""
|
|
131
|
+
return {
|
|
132
|
+
item.document_id: item.payload
|
|
133
|
+
for item in self._sessions.values()
|
|
134
|
+
if item.payload is not None
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
def previews(self) -> dict[str, DocumentPreview]:
|
|
138
|
+
"""Return previews keyed by document id."""
|
|
139
|
+
now = datetime.now(UTC).isoformat()
|
|
140
|
+
result: dict[str, DocumentPreview] = {}
|
|
141
|
+
for item in self._sessions.values():
|
|
142
|
+
if item.preview is not None:
|
|
143
|
+
result[item.document_id] = item.preview
|
|
144
|
+
elif item.payload is not None:
|
|
145
|
+
# Fallback text preview for UTF-8 payloads.
|
|
146
|
+
try:
|
|
147
|
+
body = item.payload.decode("utf-8")
|
|
148
|
+
except UnicodeDecodeError:
|
|
149
|
+
continue
|
|
150
|
+
result[item.document_id] = DocumentPreview(
|
|
151
|
+
documentId=item.document_id,
|
|
152
|
+
mode="text",
|
|
153
|
+
body=body,
|
|
154
|
+
warnings=[],
|
|
155
|
+
generatedAt=now,
|
|
156
|
+
renderer={
|
|
157
|
+
"package": item.driver.driver_id,
|
|
158
|
+
"version": item.driver.manifest.version,
|
|
159
|
+
},
|
|
160
|
+
)
|
|
161
|
+
return result
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"""Load template bytes and manifests from a project package directory."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
from roborean_spec import Project, TemplateManifest
|
|
7
|
+
|
|
8
|
+
from .errors import TemplateError
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class DocumentTemplateStore:
|
|
12
|
+
"""Resolve templates by id from a project package root."""
|
|
13
|
+
|
|
14
|
+
def __init__(self, package_dir: Path, project: Project) -> None:
|
|
15
|
+
"""Bind to a package directory and project template table."""
|
|
16
|
+
self.package_dir = package_dir
|
|
17
|
+
self.project = project
|
|
18
|
+
self._by_id = {item["id"]: item for item in project.templates}
|
|
19
|
+
|
|
20
|
+
def resolve_path(self, template_ref: str) -> Path:
|
|
21
|
+
"""Return the absolute path for a template id."""
|
|
22
|
+
entry = self._by_id.get(template_ref)
|
|
23
|
+
if entry is None:
|
|
24
|
+
raise TemplateError(f"Unknown templateRef: {template_ref}")
|
|
25
|
+
path = self.package_dir / entry["path"]
|
|
26
|
+
if not path.is_file():
|
|
27
|
+
raise TemplateError(f"Missing template file: {path}")
|
|
28
|
+
return path
|
|
29
|
+
|
|
30
|
+
def load_bytes(self, template_ref: str) -> bytes:
|
|
31
|
+
"""Read template bytes."""
|
|
32
|
+
return self.resolve_path(template_ref).read_bytes()
|
|
33
|
+
|
|
34
|
+
def load_manifest(
|
|
35
|
+
self,
|
|
36
|
+
template_ref: str,
|
|
37
|
+
*,
|
|
38
|
+
manifest_ref: str | None = None,
|
|
39
|
+
) -> TemplateManifest:
|
|
40
|
+
"""Load the sidecar template manifest."""
|
|
41
|
+
if manifest_ref:
|
|
42
|
+
path = self.package_dir / manifest_ref
|
|
43
|
+
else:
|
|
44
|
+
template_path = self.resolve_path(template_ref)
|
|
45
|
+
path = template_path.with_suffix(
|
|
46
|
+
template_path.suffix + ".manifest.json"
|
|
47
|
+
)
|
|
48
|
+
if not path.is_file():
|
|
49
|
+
# Also accept templates/<id>.manifest.json next to stem.
|
|
50
|
+
path = template_path.with_name(
|
|
51
|
+
template_path.stem + ".manifest.json"
|
|
52
|
+
)
|
|
53
|
+
if not path.is_file():
|
|
54
|
+
raise TemplateError(f"Missing template manifest: {path}")
|
|
55
|
+
data = json.loads(path.read_text(encoding="utf-8"))
|
|
56
|
+
return TemplateManifest.model_validate(data)
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: roborean-documents-base
|
|
3
|
+
Version: 0.1.2
|
|
4
|
+
Summary: Document driver protocols and session manager for Roborean
|
|
5
|
+
Project-URL: Homepage, https://github.com/TNick/roborean
|
|
6
|
+
Project-URL: Repository, https://github.com/TNick/roborean
|
|
7
|
+
License-Expression: MIT
|
|
8
|
+
Requires-Python: >=3.11
|
|
9
|
+
Requires-Dist: roborean-spec>=0.1.1
|
|
10
|
+
Provides-Extra: dev
|
|
11
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
|
|
14
|
+
# roborean-documents-base
|
|
15
|
+
|
|
16
|
+
Protocols and session orchestration for Roborean document drivers.
|
|
17
|
+
|
|
18
|
+
Bits emit typed document operations; drivers load templates and serialize
|
|
19
|
+
artifacts. Format packages implement `DocumentDriver`.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
roborean_documents_base/__init__.py,sha256=dqT2QV8Y0FRrfXFaLMDm1TYpVfURDTuKn4UYHwrIt6A,716
|
|
2
|
+
roborean_documents_base/artifact.py,sha256=kkemUA_vzn70rb1G5efUOL-XOBJxGpWHx7M98z66ChE,398
|
|
3
|
+
roborean_documents_base/capabilities.py,sha256=sWAWo3zNjpCODgSFzCQdk4yc-7PgQGN7cNuF7enUzjE,908
|
|
4
|
+
roborean_documents_base/errors.py,sha256=Nmqi3cO2Nei6pmBmfBLK1CxqcuRDS5yw2HjDk401-aM,294
|
|
5
|
+
roborean_documents_base/protocol.py,sha256=cahDWWa7O3OEMcBb0jEXHB53aaZ-jWLqml3J722O80E,1600
|
|
6
|
+
roborean_documents_base/registry.py,sha256=TioO4DfYhTCzzDflZgOLlDvTbML3Jy3xIh7mNlNomWs,2198
|
|
7
|
+
roborean_documents_base/resolve_values.py,sha256=RgtitC-tSNl7S_6iVbmkg-S747GAkmvjslPuI-vcfYs,887
|
|
8
|
+
roborean_documents_base/session.py,sha256=h1LeUhXBB1qsRr8BJTvG88SHgix9aMt080EqikLyir0,5735
|
|
9
|
+
roborean_documents_base/template_store.py,sha256=YKlEgSyJ_9Dk6NSRNzu_epThrFVuQLc3EqwIuW6LGco,2054
|
|
10
|
+
roborean_documents_base-0.1.2.dist-info/METADATA,sha256=KxgZm0Z5nhubskKtlp24qMXDVrU_u3SRHFkCuDA1i3c,663
|
|
11
|
+
roborean_documents_base-0.1.2.dist-info/WHEEL,sha256=lCkmxWfQsSc9CfIClYeavTdQeEX2toPqufh9gI35EQA,87
|
|
12
|
+
roborean_documents_base-0.1.2.dist-info/RECORD,,
|