cox-protocol-package 1.0.2 → 1.2.0
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.
- package/CHANGELOG.md +22 -0
- package/COX_FILE_FORMAT_SPECIFICATION_v1.0.md +40 -200
- package/FINALIZATION.md +50 -0
- package/LocalAppManager/Dashboard/analytics_sidebar_panels/analytics_sidebar_panels.cox +56 -0
- package/LocalAppManager/Dashboard/analytics_sidebar_panels/analytics_sidebar_panels_screenshot.placeholder.txt +1 -0
- package/LocalAppManager/Dashboard/dashboard.cox +62 -0
- package/LocalAppManager/Dashboard/dashboard_screenshot.placeholder.txt +1 -0
- package/LocalAppManager/Dashboard/left_navigation_sidebar/left_navigation_sidebar.cox +56 -0
- package/LocalAppManager/Dashboard/left_navigation_sidebar/left_navigation_sidebar_screenshot.placeholder.txt +1 -0
- package/LocalAppManager/Dashboard/projects_grid_cards/projects_grid_cards.cox +56 -0
- package/LocalAppManager/Dashboard/projects_grid_cards/projects_grid_cards_screenshot.placeholder.txt +1 -0
- package/LocalAppManager/Dashboard/top_header_stats_bar/top_header_stats_bar.cox +56 -0
- package/LocalAppManager/Dashboard/top_header_stats_bar/top_header_stats_bar_screenshot.placeholder.txt +1 -0
- package/MANIFEST.in +20 -0
- package/README.md +67 -154
- package/bin/cox-protocol.js +23 -97
- package/cox_protocol/__init__.py +2 -0
- package/cox_protocol/__main__.py +3 -0
- package/cox_protocol/compiler.py +273 -0
- package/cox_protocol/cox_cli.py +146 -0
- package/cox_protocol/cox_lib.py +599 -0
- package/cox_protocol/package_target.py +262 -0
- package/cox_protocol/react_target.py +306 -0
- package/cox_protocol/scaffold.py +56 -0
- package/cox_protocol/schemas/cox.component.v1.schema.json +180 -0
- package/cox_protocol/schemas/cox.crop_metadata.v1.schema.json +83 -0
- package/cox_protocol/schemas/cox.manifest.v1.schema.json +172 -0
- package/cox_protocol/schemas/cox.page.v1.schema.json +242 -0
- package/cox_protocol/schemas/cox.protocol.v1.schema.json +38 -0
- package/cox_protocol/starter/README.md +7 -0
- package/cox_protocol/starter/manifest.cox +26 -0
- package/cox_protocol/starter/page/action_button/action_button.cox +34 -0
- package/cox_protocol/starter/page/action_button/reference.svg +1 -0
- package/cox_protocol/starter/page/page.cox +39 -0
- package/cox_protocol/starter/page/reference.svg +1 -0
- package/editor-support/vscode/CHANGELOG.md +6 -0
- package/editor-support/vscode/README.md +2 -1
- package/editor-support/vscode/package.json +3 -3
- package/editor-support/vscode/snippets/cox-common.json +1 -1
- package/editor-support/vscode/snippets/cox-component.json +2 -1
- package/editor-support/vscode/snippets/cox-crop-metadata.json +1 -1
- package/editor-support/vscode/snippets/cox-manifest.json +1 -1
- package/editor-support/vscode/snippets/cox-page.json +2 -1
- package/editor-support/vscode/snippets/cox-protocol.json +1 -1
- package/examples/quickstart/README.md +7 -0
- package/examples/quickstart/manifest.cox +26 -0
- package/examples/quickstart/page/action_button/action_button.cox +34 -0
- package/examples/quickstart/page/action_button/reference.svg +1 -0
- package/examples/quickstart/page/page.cox +39 -0
- package/examples/quickstart/page/reference.svg +1 -0
- package/guides/contract-demo.md +24 -0
- package/guides/npm-installation.md +16 -287
- package/manifest.cox +7 -4
- package/package.json +30 -14
- package/protocols/recursive_decomposition_protocol.cox +8 -8
- package/pyproject.toml +17 -6
- package/schemas/cox.component.v1.schema.json +17 -4
- package/schemas/cox.crop_metadata.v1.schema.json +1 -1
- package/schemas/cox.manifest.v1.schema.json +1 -1
- package/schemas/cox.page.v1.schema.json +17 -4
- package/schemas/cox.protocol.v1.schema.json +1 -1
- package/scripts/check_distribution.py +87 -0
- package/scripts/demo.py +62 -0
- package/scripts/package-vscode.mjs +12 -0
- package/scripts/release_check.py +86 -0
- package/tests/conftest.py +21 -0
- package/tests/test_cli_and_typescript.py +123 -0
- package/tests/test_compiler.py +279 -0
- package/tests/test_validation.py +215 -0
- package/tools/__init__.py +2 -2
- package/tools/cox_cli.py +2 -123
- package/tools/cox_lib.py +2 -558
- package/tools/package_target.py +2 -214
- package/tools/react_target.py +2 -278
package/tools/cox_lib.py
CHANGED
|
@@ -1,559 +1,3 @@
|
|
|
1
|
-
|
|
1
|
+
"""Compatibility import for the original source checkout. Use cox_protocol."""
|
|
2
2
|
|
|
3
|
-
from
|
|
4
|
-
|
|
5
|
-
import json
|
|
6
|
-
from dataclasses import dataclass
|
|
7
|
-
from pathlib import Path
|
|
8
|
-
from typing import Any, Sequence
|
|
9
|
-
|
|
10
|
-
import yaml # type: ignore[import-untyped]
|
|
11
|
-
from jsonschema import Draft202012Validator # type: ignore[import-untyped]
|
|
12
|
-
|
|
13
|
-
from . import __version__
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
SCHEMA_FILENAMES = {
|
|
17
|
-
"cox.manifest.v1": "cox.manifest.v1.schema.json",
|
|
18
|
-
"cox.page.v1": "cox.page.v1.schema.json",
|
|
19
|
-
"cox.component.v1": "cox.component.v1.schema.json",
|
|
20
|
-
"cox.protocol.v1": "cox.protocol.v1.schema.json",
|
|
21
|
-
"cox.crop_metadata.v1": "cox.crop_metadata.v1.schema.json",
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
SUPPORTED_COMPILE_TARGETS = ("json", "react", "package")
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
class CoxError(RuntimeError):
|
|
28
|
-
"""Raised when COX validation or compilation fails."""
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
@dataclass
|
|
32
|
-
class ValidationSummary:
|
|
33
|
-
project_root: Path
|
|
34
|
-
manifest_path: Path
|
|
35
|
-
documents: dict[Path, dict[str, Any]]
|
|
36
|
-
referenced_paths: list[Path]
|
|
37
|
-
warnings: list[str]
|
|
38
|
-
build_index: dict[str, Any]
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
def resolve_project_root(target: Path) -> tuple[Path, Path]:
|
|
42
|
-
resolved_target = target.resolve()
|
|
43
|
-
search_root = (
|
|
44
|
-
resolved_target
|
|
45
|
-
if resolved_target.is_dir()
|
|
46
|
-
else resolved_target.parent
|
|
47
|
-
)
|
|
48
|
-
|
|
49
|
-
direct_manifest = search_root / "manifest.cox"
|
|
50
|
-
if direct_manifest.exists():
|
|
51
|
-
return search_root, direct_manifest
|
|
52
|
-
|
|
53
|
-
for candidate_root in [search_root, *search_root.parents]:
|
|
54
|
-
manifest_path = candidate_root / "manifest.cox"
|
|
55
|
-
if manifest_path.exists():
|
|
56
|
-
return candidate_root, manifest_path
|
|
57
|
-
|
|
58
|
-
raise CoxError(f"Could not locate manifest.cox from {target}")
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
def read_yaml_document(path: Path) -> dict[str, Any]:
|
|
62
|
-
try:
|
|
63
|
-
content = path.read_text(encoding="utf-8")
|
|
64
|
-
except UnicodeDecodeError as exc:
|
|
65
|
-
raise CoxError(f"{path}: file is not valid UTF-8") from exc
|
|
66
|
-
|
|
67
|
-
try:
|
|
68
|
-
document = yaml.safe_load(content)
|
|
69
|
-
except yaml.YAMLError as exc:
|
|
70
|
-
raise CoxError(f"{path}: YAML parsing failed: {exc}") from exc
|
|
71
|
-
|
|
72
|
-
if not isinstance(document, dict):
|
|
73
|
-
raise CoxError(f"{path}: top-level COX document must be a mapping")
|
|
74
|
-
|
|
75
|
-
return document
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
def read_json_document(path: Path) -> dict[str, Any]:
|
|
79
|
-
try:
|
|
80
|
-
return json.loads(path.read_text(encoding="utf-8"))
|
|
81
|
-
except json.JSONDecodeError as exc:
|
|
82
|
-
raise CoxError(f"{path}: JSON parsing failed: {exc}") from exc
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
def load_schema(schema_dir: Path, schema_name: str) -> dict[str, Any]:
|
|
86
|
-
schema_file = SCHEMA_FILENAMES.get(schema_name)
|
|
87
|
-
if schema_file is None:
|
|
88
|
-
raise CoxError(f"Unknown COX schema id: {schema_name}")
|
|
89
|
-
|
|
90
|
-
schema_path = schema_dir / schema_file
|
|
91
|
-
if not schema_path.exists():
|
|
92
|
-
raise CoxError(f"Schema file is missing: {schema_path}")
|
|
93
|
-
|
|
94
|
-
return read_json_document(schema_path)
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
def relative_path(root: Path, path: Path) -> str:
|
|
98
|
-
return path.resolve().relative_to(root.resolve()).as_posix()
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
def format_report_path(root: Path, path: Path) -> str:
|
|
102
|
-
try:
|
|
103
|
-
return relative_path(root, path)
|
|
104
|
-
except ValueError:
|
|
105
|
-
return path.resolve().as_posix()
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
def unique_paths(paths: list[Path]) -> list[Path]:
|
|
109
|
-
ordered: list[Path] = []
|
|
110
|
-
seen: set[Path] = set()
|
|
111
|
-
for path in paths:
|
|
112
|
-
resolved = path.resolve()
|
|
113
|
-
if resolved in seen:
|
|
114
|
-
continue
|
|
115
|
-
seen.add(resolved)
|
|
116
|
-
ordered.append(resolved)
|
|
117
|
-
return ordered
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
def validate_against_schema(
|
|
121
|
-
document: dict[str, Any],
|
|
122
|
-
path: Path,
|
|
123
|
-
schema_dir: Path,
|
|
124
|
-
) -> list[str]:
|
|
125
|
-
schema_name = document.get("Schema")
|
|
126
|
-
if not isinstance(schema_name, str) or not schema_name:
|
|
127
|
-
return [f"{path}: missing Schema field"]
|
|
128
|
-
|
|
129
|
-
schema = load_schema(schema_dir, schema_name)
|
|
130
|
-
validator = Draft202012Validator(schema)
|
|
131
|
-
issues: list[str] = []
|
|
132
|
-
|
|
133
|
-
sorted_errors = sorted(
|
|
134
|
-
validator.iter_errors(document),
|
|
135
|
-
key=lambda item: list(item.absolute_path),
|
|
136
|
-
)
|
|
137
|
-
for error in sorted_errors:
|
|
138
|
-
location = ".".join(str(part) for part in error.absolute_path)
|
|
139
|
-
prefix = f"{path}: {location}" if location else str(path)
|
|
140
|
-
issues.append(f"{prefix}: {error.message}")
|
|
141
|
-
|
|
142
|
-
return issues
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
def build_manifest_index(
|
|
146
|
-
manifest: dict[str, Any],
|
|
147
|
-
project_root: Path,
|
|
148
|
-
) -> dict[str, Any]:
|
|
149
|
-
reference_docs = [
|
|
150
|
-
project_root / path for path in manifest.get("ReferenceDocs", [])
|
|
151
|
-
]
|
|
152
|
-
guide_docs = [
|
|
153
|
-
project_root / path for path in manifest.get("GuideDocs", [])
|
|
154
|
-
]
|
|
155
|
-
protocol_docs = [
|
|
156
|
-
project_root / path for path in manifest.get("ProtocolDocs", [])
|
|
157
|
-
]
|
|
158
|
-
skill_bundles = [
|
|
159
|
-
project_root / path for path in manifest.get("SkillBundles", [])
|
|
160
|
-
]
|
|
161
|
-
schema_files = [
|
|
162
|
-
project_root / path for path in manifest.get("Schemas", [])
|
|
163
|
-
]
|
|
164
|
-
tooling_files = [
|
|
165
|
-
project_root / path for path in manifest.get("ToolingFiles", [])
|
|
166
|
-
]
|
|
167
|
-
|
|
168
|
-
page_entries: list[dict[str, Any]] = []
|
|
169
|
-
cox_documents = protocol_docs.copy()
|
|
170
|
-
for optional_cox_path in [*guide_docs, *skill_bundles]:
|
|
171
|
-
if optional_cox_path.suffix == ".cox":
|
|
172
|
-
cox_documents.append(optional_cox_path)
|
|
173
|
-
elif optional_cox_path.exists() and optional_cox_path.is_dir():
|
|
174
|
-
cox_documents.extend(optional_cox_path.rglob("*.cox"))
|
|
175
|
-
referenced_paths = (
|
|
176
|
-
reference_docs
|
|
177
|
-
+ guide_docs
|
|
178
|
-
+ protocol_docs
|
|
179
|
-
+ skill_bundles
|
|
180
|
-
+ schema_files
|
|
181
|
-
+ tooling_files
|
|
182
|
-
)
|
|
183
|
-
|
|
184
|
-
for page in manifest.get("Pages", []):
|
|
185
|
-
page_instruction = project_root / page["PageInstruction"]
|
|
186
|
-
page_screenshot = project_root / page["PageScreenshotPlaceholder"]
|
|
187
|
-
referenced_paths.extend([page_instruction, page_screenshot])
|
|
188
|
-
cox_documents.append(page_instruction)
|
|
189
|
-
|
|
190
|
-
component_entries: list[dict[str, Any]] = []
|
|
191
|
-
for component in page.get("Components", []):
|
|
192
|
-
component_instruction = (
|
|
193
|
-
project_root / component["ComponentInstruction"]
|
|
194
|
-
)
|
|
195
|
-
component_screenshot = (
|
|
196
|
-
project_root / component["ComponentScreenshotPlaceholder"]
|
|
197
|
-
)
|
|
198
|
-
referenced_paths.extend(
|
|
199
|
-
[component_instruction, component_screenshot]
|
|
200
|
-
)
|
|
201
|
-
cox_documents.append(component_instruction)
|
|
202
|
-
component_entries.append(
|
|
203
|
-
{
|
|
204
|
-
"slug": component["Slug"],
|
|
205
|
-
"folder": component["Folder"],
|
|
206
|
-
"instruction": relative_path(
|
|
207
|
-
project_root,
|
|
208
|
-
component_instruction,
|
|
209
|
-
),
|
|
210
|
-
"screenshot": relative_path(
|
|
211
|
-
project_root,
|
|
212
|
-
component_screenshot,
|
|
213
|
-
),
|
|
214
|
-
}
|
|
215
|
-
)
|
|
216
|
-
|
|
217
|
-
page_entries.append(
|
|
218
|
-
{
|
|
219
|
-
"pageName": page["PageName"],
|
|
220
|
-
"slug": page["Slug"],
|
|
221
|
-
"folder": page["Folder"],
|
|
222
|
-
"instruction": relative_path(project_root, page_instruction),
|
|
223
|
-
"screenshot": relative_path(project_root, page_screenshot),
|
|
224
|
-
"components": component_entries,
|
|
225
|
-
}
|
|
226
|
-
)
|
|
227
|
-
|
|
228
|
-
return {
|
|
229
|
-
"reference_docs": [
|
|
230
|
-
relative_path(project_root, path) for path in reference_docs
|
|
231
|
-
],
|
|
232
|
-
"guide_docs": [
|
|
233
|
-
relative_path(project_root, path) for path in guide_docs
|
|
234
|
-
],
|
|
235
|
-
"protocol_docs": [
|
|
236
|
-
relative_path(project_root, path) for path in protocol_docs
|
|
237
|
-
],
|
|
238
|
-
"skill_bundles": [
|
|
239
|
-
relative_path(project_root, path) for path in skill_bundles
|
|
240
|
-
],
|
|
241
|
-
"schema_files": [
|
|
242
|
-
relative_path(project_root, path) for path in schema_files
|
|
243
|
-
],
|
|
244
|
-
"tooling_files": [
|
|
245
|
-
relative_path(project_root, path) for path in tooling_files
|
|
246
|
-
],
|
|
247
|
-
"pages": page_entries,
|
|
248
|
-
"cox_documents": unique_paths(
|
|
249
|
-
[project_root / "manifest.cox", *cox_documents]
|
|
250
|
-
),
|
|
251
|
-
"referenced_paths": unique_paths(referenced_paths),
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
def validate_project(target: Path) -> ValidationSummary:
|
|
256
|
-
project_root, manifest_path = resolve_project_root(target)
|
|
257
|
-
schema_dir = project_root / "schemas"
|
|
258
|
-
manifest = read_yaml_document(manifest_path)
|
|
259
|
-
|
|
260
|
-
issues = validate_against_schema(manifest, manifest_path, schema_dir)
|
|
261
|
-
manifest_index = build_manifest_index(manifest, project_root)
|
|
262
|
-
|
|
263
|
-
missing_paths = [
|
|
264
|
-
path
|
|
265
|
-
for path in manifest_index["referenced_paths"]
|
|
266
|
-
if not path.exists()
|
|
267
|
-
]
|
|
268
|
-
if missing_paths:
|
|
269
|
-
missing_lines = [
|
|
270
|
-
f"Missing referenced path: {relative_path(project_root, path)}"
|
|
271
|
-
for path in missing_paths
|
|
272
|
-
]
|
|
273
|
-
missing_text = "\n".join(missing_lines)
|
|
274
|
-
issues.append(missing_text)
|
|
275
|
-
|
|
276
|
-
documents: dict[Path, dict[str, Any]] = {manifest_path.resolve(): manifest}
|
|
277
|
-
for document_path in manifest_index["cox_documents"]:
|
|
278
|
-
document = read_yaml_document(document_path)
|
|
279
|
-
documents[document_path.resolve()] = document
|
|
280
|
-
issues.extend(
|
|
281
|
-
validate_against_schema(document, document_path, schema_dir)
|
|
282
|
-
)
|
|
283
|
-
|
|
284
|
-
discovered_cox_files = [
|
|
285
|
-
path.resolve()
|
|
286
|
-
for path in project_root.rglob("*.cox")
|
|
287
|
-
if "build" not in path.parts
|
|
288
|
-
]
|
|
289
|
-
referenced_cox_files = set(manifest_index["cox_documents"])
|
|
290
|
-
unreferenced_files = [
|
|
291
|
-
path
|
|
292
|
-
for path in discovered_cox_files
|
|
293
|
-
if path not in referenced_cox_files
|
|
294
|
-
]
|
|
295
|
-
warnings: list[str] = []
|
|
296
|
-
if unreferenced_files:
|
|
297
|
-
unreferenced_text = ", ".join(
|
|
298
|
-
relative_path(project_root, path)
|
|
299
|
-
for path in sorted(unreferenced_files)
|
|
300
|
-
)
|
|
301
|
-
warnings.append(
|
|
302
|
-
"Unreferenced COX files: " + unreferenced_text
|
|
303
|
-
)
|
|
304
|
-
|
|
305
|
-
if issues:
|
|
306
|
-
raise CoxError("\n".join(issues))
|
|
307
|
-
|
|
308
|
-
return ValidationSummary(
|
|
309
|
-
project_root=project_root,
|
|
310
|
-
manifest_path=manifest_path.resolve(),
|
|
311
|
-
documents=documents,
|
|
312
|
-
referenced_paths=manifest_index["referenced_paths"],
|
|
313
|
-
warnings=warnings,
|
|
314
|
-
build_index=manifest_index,
|
|
315
|
-
)
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
def write_json(path: Path, payload: dict[str, Any]) -> None:
|
|
319
|
-
path.parent.mkdir(parents=True, exist_ok=True)
|
|
320
|
-
serialized = json.dumps(payload, indent=2, ensure_ascii=True) + "\n"
|
|
321
|
-
path.write_text(serialized, encoding="utf-8")
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
def normalize_compile_targets(targets: Sequence[str] | None) -> list[str]:
|
|
325
|
-
requested_targets = list(targets or ["json"])
|
|
326
|
-
expanded_targets: list[str] = []
|
|
327
|
-
|
|
328
|
-
for raw_target in requested_targets:
|
|
329
|
-
for piece in raw_target.split(","):
|
|
330
|
-
target = piece.strip().lower()
|
|
331
|
-
if not target:
|
|
332
|
-
continue
|
|
333
|
-
if target == "all":
|
|
334
|
-
expanded_targets.extend(SUPPORTED_COMPILE_TARGETS)
|
|
335
|
-
continue
|
|
336
|
-
expanded_targets.append(target)
|
|
337
|
-
|
|
338
|
-
normalized_targets: list[str] = []
|
|
339
|
-
for target in expanded_targets or ["json"]:
|
|
340
|
-
if target not in SUPPORTED_COMPILE_TARGETS:
|
|
341
|
-
supported = ", ".join(SUPPORTED_COMPILE_TARGETS)
|
|
342
|
-
raise CoxError(
|
|
343
|
-
f"Unsupported compile target '{target}'. "
|
|
344
|
-
f"Supported targets: {supported}"
|
|
345
|
-
)
|
|
346
|
-
if target not in normalized_targets:
|
|
347
|
-
normalized_targets.append(target)
|
|
348
|
-
|
|
349
|
-
return normalized_targets
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
def emit_json_target(
|
|
353
|
-
summary: ValidationSummary,
|
|
354
|
-
build_root: Path,
|
|
355
|
-
) -> dict[str, Any]:
|
|
356
|
-
normalized_root = build_root / "normalized"
|
|
357
|
-
|
|
358
|
-
for document_path, payload in summary.documents.items():
|
|
359
|
-
if document_path == summary.manifest_path:
|
|
360
|
-
continue
|
|
361
|
-
output_path = normalized_root / document_path.relative_to(
|
|
362
|
-
summary.project_root
|
|
363
|
-
)
|
|
364
|
-
write_json(output_path.with_suffix(".json"), payload)
|
|
365
|
-
|
|
366
|
-
return {
|
|
367
|
-
"output_root": normalized_root,
|
|
368
|
-
"document_count": len(summary.documents) - 1,
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
def build_report(
|
|
373
|
-
summary: ValidationSummary,
|
|
374
|
-
build_root: Path,
|
|
375
|
-
target_results: dict[str, dict[str, Any]],
|
|
376
|
-
) -> str:
|
|
377
|
-
warning_lines = summary.warnings or ["none"]
|
|
378
|
-
compiled_manifest = format_report_path(
|
|
379
|
-
summary.project_root,
|
|
380
|
-
build_root / "manifest.compiled.json",
|
|
381
|
-
)
|
|
382
|
-
build_root_label = format_report_path(summary.project_root, build_root)
|
|
383
|
-
verification_report = format_report_path(
|
|
384
|
-
summary.project_root,
|
|
385
|
-
build_root / "reports" / "verification_report.txt",
|
|
386
|
-
)
|
|
387
|
-
report_lines = [
|
|
388
|
-
"CompiledVerificationReport:",
|
|
389
|
-
' CoxVersion: "1.0"',
|
|
390
|
-
f' ProjectRoot: "{summary.project_root.name}"',
|
|
391
|
-
(
|
|
392
|
-
" Manifest: "
|
|
393
|
-
f'"{relative_path(summary.project_root, summary.manifest_path)}"'
|
|
394
|
-
),
|
|
395
|
-
f' ValidatedDocuments: {len(summary.documents)}',
|
|
396
|
-
f' GuideDocs: {len(summary.build_index["guide_docs"])}',
|
|
397
|
-
f' SkillBundles: {len(summary.build_index["skill_bundles"])}',
|
|
398
|
-
f' CompiledManifest: "{compiled_manifest}"',
|
|
399
|
-
f' ReferencedPathsChecked: {len(summary.referenced_paths)}',
|
|
400
|
-
" Targets:",
|
|
401
|
-
]
|
|
402
|
-
report_lines.extend(
|
|
403
|
-
f' - "{target_name}"' for target_name in target_results
|
|
404
|
-
)
|
|
405
|
-
report_lines.append(" Warnings:")
|
|
406
|
-
report_lines.extend(f' - "{line}"' for line in warning_lines)
|
|
407
|
-
report_lines.extend(
|
|
408
|
-
[
|
|
409
|
-
" Outputs:",
|
|
410
|
-
f' BuildRoot: "{build_root_label}"',
|
|
411
|
-
f' VerificationReport: "{verification_report}"',
|
|
412
|
-
f' ManifestCompiledJson: "{compiled_manifest}"',
|
|
413
|
-
]
|
|
414
|
-
)
|
|
415
|
-
|
|
416
|
-
json_result = target_results.get("json")
|
|
417
|
-
if json_result is not None:
|
|
418
|
-
json_output_root = format_report_path(
|
|
419
|
-
summary.project_root,
|
|
420
|
-
json_result["output_root"],
|
|
421
|
-
)
|
|
422
|
-
report_lines.extend(
|
|
423
|
-
[
|
|
424
|
-
" JsonTarget:",
|
|
425
|
-
f' OutputRoot: "{json_output_root}"',
|
|
426
|
-
(
|
|
427
|
-
" DocumentCount: "
|
|
428
|
-
f'{json_result["document_count"]}'
|
|
429
|
-
),
|
|
430
|
-
]
|
|
431
|
-
)
|
|
432
|
-
|
|
433
|
-
react_result = target_results.get("react")
|
|
434
|
-
if react_result is not None:
|
|
435
|
-
react_output_root = format_report_path(
|
|
436
|
-
summary.project_root,
|
|
437
|
-
react_result["output_root"],
|
|
438
|
-
)
|
|
439
|
-
react_manifest = format_report_path(
|
|
440
|
-
summary.project_root,
|
|
441
|
-
react_result["manifest"],
|
|
442
|
-
)
|
|
443
|
-
report_lines.extend(
|
|
444
|
-
[
|
|
445
|
-
" ReactTarget:",
|
|
446
|
-
f' OutputRoot: "{react_output_root}"',
|
|
447
|
-
(
|
|
448
|
-
" PageCount: "
|
|
449
|
-
f'{react_result["page_count"]}'
|
|
450
|
-
),
|
|
451
|
-
(
|
|
452
|
-
" ComponentCount: "
|
|
453
|
-
f'{react_result["component_count"]}'
|
|
454
|
-
),
|
|
455
|
-
(
|
|
456
|
-
" Manifest: "
|
|
457
|
-
f'"{react_manifest}"'
|
|
458
|
-
),
|
|
459
|
-
]
|
|
460
|
-
)
|
|
461
|
-
|
|
462
|
-
package_result = target_results.get("package")
|
|
463
|
-
if package_result is not None:
|
|
464
|
-
package_output_root = format_report_path(
|
|
465
|
-
summary.project_root,
|
|
466
|
-
package_result["output_root"],
|
|
467
|
-
)
|
|
468
|
-
package_archive = format_report_path(
|
|
469
|
-
summary.project_root,
|
|
470
|
-
package_result["archive"],
|
|
471
|
-
)
|
|
472
|
-
package_manifest = format_report_path(
|
|
473
|
-
summary.project_root,
|
|
474
|
-
package_result["manifest"],
|
|
475
|
-
)
|
|
476
|
-
report_lines.extend(
|
|
477
|
-
[
|
|
478
|
-
" PackageTarget:",
|
|
479
|
-
f' OutputRoot: "{package_output_root}"',
|
|
480
|
-
f' Archive: "{package_archive}"',
|
|
481
|
-
f' Manifest: "{package_manifest}"',
|
|
482
|
-
f' FileCount: {package_result["file_count"]}',
|
|
483
|
-
f' Sha256: "{package_result["sha256"]}"',
|
|
484
|
-
]
|
|
485
|
-
)
|
|
486
|
-
|
|
487
|
-
return "\n".join(report_lines) + "\n"
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
def compile_project(
|
|
491
|
-
target: Path,
|
|
492
|
-
out_dir: Path,
|
|
493
|
-
targets: Sequence[str] | None = None,
|
|
494
|
-
) -> dict[str, Any]:
|
|
495
|
-
summary = validate_project(target)
|
|
496
|
-
compile_targets = normalize_compile_targets(targets)
|
|
497
|
-
build_root = (
|
|
498
|
-
out_dir if out_dir.is_absolute() else summary.project_root / out_dir
|
|
499
|
-
)
|
|
500
|
-
reports_root = build_root / "reports"
|
|
501
|
-
target_results: dict[str, dict[str, Any]] = {}
|
|
502
|
-
|
|
503
|
-
if "json" in compile_targets:
|
|
504
|
-
target_results["json"] = emit_json_target(summary, build_root)
|
|
505
|
-
|
|
506
|
-
if "react" in compile_targets:
|
|
507
|
-
from .react_target import emit_react_stubs
|
|
508
|
-
|
|
509
|
-
target_results["react"] = emit_react_stubs(summary, build_root)
|
|
510
|
-
|
|
511
|
-
if "package" in compile_targets:
|
|
512
|
-
from .package_target import emit_package_archive
|
|
513
|
-
|
|
514
|
-
target_results["package"] = emit_package_archive(
|
|
515
|
-
summary,
|
|
516
|
-
build_root,
|
|
517
|
-
target_results,
|
|
518
|
-
)
|
|
519
|
-
|
|
520
|
-
manifest_payload = {
|
|
521
|
-
"compiledWith": f"cox {__version__}",
|
|
522
|
-
"mimeType": "application/vnd.cox+yaml",
|
|
523
|
-
"targets": compile_targets,
|
|
524
|
-
"manifest": summary.documents[summary.manifest_path],
|
|
525
|
-
"resolvedFiles": {
|
|
526
|
-
"referenceDocs": summary.build_index["reference_docs"],
|
|
527
|
-
"guideDocs": summary.build_index["guide_docs"],
|
|
528
|
-
"protocolDocs": summary.build_index["protocol_docs"],
|
|
529
|
-
"skillBundles": summary.build_index["skill_bundles"],
|
|
530
|
-
"schemaFiles": summary.build_index["schema_files"],
|
|
531
|
-
"toolingFiles": summary.build_index["tooling_files"],
|
|
532
|
-
"pages": summary.build_index["pages"],
|
|
533
|
-
},
|
|
534
|
-
"targetOutputs": {
|
|
535
|
-
target_name: {
|
|
536
|
-
key: value.as_posix() if isinstance(value, Path) else value
|
|
537
|
-
for key, value in result.items()
|
|
538
|
-
}
|
|
539
|
-
for target_name, result in target_results.items()
|
|
540
|
-
},
|
|
541
|
-
"warnings": summary.warnings,
|
|
542
|
-
}
|
|
543
|
-
write_json(build_root / "manifest.compiled.json", manifest_payload)
|
|
544
|
-
|
|
545
|
-
reports_root.mkdir(parents=True, exist_ok=True)
|
|
546
|
-
report_path = reports_root / "verification_report.txt"
|
|
547
|
-
report_path.write_text(
|
|
548
|
-
build_report(summary, build_root, target_results),
|
|
549
|
-
encoding="utf-8",
|
|
550
|
-
)
|
|
551
|
-
|
|
552
|
-
return {
|
|
553
|
-
"build_root": build_root,
|
|
554
|
-
"targets": compile_targets,
|
|
555
|
-
"target_results": target_results,
|
|
556
|
-
"compiled_manifest": build_root / "manifest.compiled.json",
|
|
557
|
-
"report": report_path,
|
|
558
|
-
"warnings": summary.warnings,
|
|
559
|
-
}
|
|
3
|
+
from cox_protocol.cox_lib import * # noqa: F401,F403
|