codex-workflow-v2 2.0.0-beta.13.9 → 2.0.0-beta.14
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/README.md +27 -388
- package/dist/reviewer-runtime-build.json +89 -29
- package/dist/src/alpha6/adoption.d.ts +2 -0
- package/dist/src/alpha6/adoption.js +22 -0
- package/dist/src/alpha6/adoption.js.map +1 -1
- package/dist/src/alpha6/captured-check-evidence.d.ts +51 -0
- package/dist/src/alpha6/captured-check-evidence.js +152 -0
- package/dist/src/alpha6/captured-check-evidence.js.map +1 -0
- package/dist/src/alpha6/component-owner.d.ts +15 -1
- package/dist/src/alpha6/component-owner.js +44 -2
- package/dist/src/alpha6/component-owner.js.map +1 -1
- package/dist/src/alpha6/corrective-decision-boundary.d.ts +4 -0
- package/dist/src/alpha6/corrective-decision-boundary.js +47 -0
- package/dist/src/alpha6/corrective-decision-boundary.js.map +1 -0
- package/dist/src/alpha6/downstream-proof.d.ts +1 -25
- package/dist/src/alpha6/downstream-proof.js +0 -171
- package/dist/src/alpha6/downstream-proof.js.map +1 -1
- package/dist/src/alpha6/literal-test-invocation.d.ts +2 -0
- package/dist/src/alpha6/literal-test-invocation.js +106 -0
- package/dist/src/alpha6/literal-test-invocation.js.map +1 -0
- package/dist/src/alpha6/milestone.d.ts +2 -0
- package/dist/src/alpha6/milestone.js +10 -1
- package/dist/src/alpha6/milestone.js.map +1 -1
- package/dist/src/alpha6/navigation-dirty-carryover.d.ts +11 -0
- package/dist/src/alpha6/navigation-dirty-carryover.js +130 -0
- package/dist/src/alpha6/navigation-dirty-carryover.js.map +1 -0
- package/dist/src/alpha6/plan-integrity.js +7 -5
- package/dist/src/alpha6/plan-integrity.js.map +1 -1
- package/dist/src/alpha6/remediation.d.ts +6 -21
- package/dist/src/alpha6/remediation.js +161 -476
- package/dist/src/alpha6/remediation.js.map +1 -1
- package/dist/src/alpha6/root-cause-replan-carryover.d.ts +4 -1
- package/dist/src/alpha6/root-cause-replan-carryover.js +29 -5
- package/dist/src/alpha6/root-cause-replan-carryover.js.map +1 -1
- package/dist/src/beta1/project-transaction.d.ts +3 -1
- package/dist/src/beta1/project-transaction.js +6 -2
- package/dist/src/beta1/project-transaction.js.map +1 -1
- package/dist/src/checks/runner.d.ts +26 -0
- package/dist/src/checks/runner.js +223 -0
- package/dist/src/checks/runner.js.map +1 -0
- package/dist/src/checks/task-sync.d.ts +10 -0
- package/dist/src/checks/task-sync.js +41 -0
- package/dist/src/checks/task-sync.js.map +1 -0
- package/dist/src/checks/worker.d.ts +1 -0
- package/dist/src/checks/worker.js +154 -0
- package/dist/src/checks/worker.js.map +1 -0
- package/dist/src/cli-actions.d.ts +2 -2
- package/dist/src/cli-actions.js +3 -15
- package/dist/src/cli-actions.js.map +1 -1
- package/dist/src/cli.js +14 -98
- package/dist/src/cli.js.map +1 -1
- package/dist/src/contracts.d.ts +35 -32
- package/dist/src/dependency-provenance.d.ts +2 -2
- package/dist/src/dependency-provenance.js +12 -69
- package/dist/src/dependency-provenance.js.map +1 -1
- package/dist/src/domain/base-sync-conflict.d.ts +13 -0
- package/dist/src/domain/base-sync-conflict.js +32 -0
- package/dist/src/domain/base-sync-conflict.js.map +1 -0
- package/dist/src/domain/step-start-admission.d.ts +4 -0
- package/dist/src/domain/step-start-admission.js +9 -0
- package/dist/src/domain/step-start-admission.js.map +1 -0
- package/dist/src/domain/virgin-registration.d.ts +3 -0
- package/dist/src/domain/virgin-registration.js +65 -0
- package/dist/src/domain/virgin-registration.js.map +1 -0
- package/dist/src/errors.d.ts +1 -1
- package/dist/src/errors.js.map +1 -1
- package/dist/src/gateway-handshake.js +0 -8
- package/dist/src/gateway-handshake.js.map +1 -1
- package/dist/src/git.d.ts +3 -1
- package/dist/src/git.js +41 -14
- package/dist/src/git.js.map +1 -1
- package/dist/src/graph.js +25 -3
- package/dist/src/graph.js.map +1 -1
- package/dist/src/index.d.ts +1 -0
- package/dist/src/navigation-actions.d.ts +9 -0
- package/dist/src/navigation-actions.js +72 -0
- package/dist/src/navigation-actions.js.map +1 -0
- package/dist/src/navigation-update-artifact.d.ts +5 -0
- package/dist/src/navigation-update-artifact.js +227 -0
- package/dist/src/navigation-update-artifact.js.map +1 -0
- package/dist/src/navigation-update.d.ts +39 -0
- package/dist/src/navigation-update.js +82 -0
- package/dist/src/navigation-update.js.map +1 -0
- package/dist/src/observation.js +52 -29
- package/dist/src/observation.js.map +1 -1
- package/dist/src/observed-routes.js +5 -6
- package/dist/src/observed-routes.js.map +1 -1
- package/dist/src/pending-review-update.d.ts +0 -13
- package/dist/src/pending-review-update.js +1 -6
- package/dist/src/pending-review-update.js.map +1 -1
- package/dist/src/reviewer.d.ts +1 -1
- package/dist/src/reviewer.js +20 -17
- package/dist/src/reviewer.js.map +1 -1
- package/dist/src/runtime.d.ts +3 -0
- package/dist/src/runtime.js +6 -0
- package/dist/src/runtime.js.map +1 -0
- package/dist/src/state/corrective-replan-executor.d.ts +10 -0
- package/dist/src/state/corrective-replan-executor.js +37 -1
- package/dist/src/state/corrective-replan-executor.js.map +1 -1
- package/dist/src/state/corrective-replan-public-schema.js +15 -2
- package/dist/src/state/corrective-replan-public-schema.js.map +1 -1
- package/dist/src/state/corrective-replan-public.js +1 -1
- package/dist/src/state/corrective-replan-public.js.map +1 -1
- package/dist/src/state/corrective-replan-transaction.d.ts +1 -0
- package/dist/src/state/corrective-replan-transaction.js +12 -11
- package/dist/src/state/corrective-replan-transaction.js.map +1 -1
- package/dist/src/state/corrective-yield-transaction.d.ts +1 -0
- package/dist/src/state/corrective-yield-transaction.js +11 -10
- package/dist/src/state/corrective-yield-transaction.js.map +1 -1
- package/dist/src/state/lock.d.ts +4 -0
- package/dist/src/state/lock.js +21 -0
- package/dist/src/state/lock.js.map +1 -1
- package/dist/src/version.d.ts +1 -1
- package/dist/src/version.js +1 -1
- package/dist/src/version.js.map +1 -1
- package/dist/src/workflow-blocker-route.d.ts +8 -0
- package/dist/src/workflow-blocker-route.js +106 -0
- package/dist/src/workflow-blocker-route.js.map +1 -0
- package/dist/src/workflow.d.ts +48 -63
- package/dist/src/workflow.js +666 -1460
- package/dist/src/workflow.js.map +1 -1
- package/docs/autonomy-guardrails.md +26 -304
- package/docs/decisions.md +14 -112
- package/docs/development-flow.md +35 -238
- package/docs/project-memory.md +31 -50
- package/docs/release-app-evidence.md +190 -0
- package/docs/release.md +95 -388
- package/docs/updating-existing-project.md +19 -717
- package/package.json +11 -13
- package/plugins/codex-workflow-gateway/.codex-plugin/plugin.json +2 -2
- package/plugins/codex-workflow-gateway/references/chat-dispatch.md +73 -197
- package/plugins/codex-workflow-gateway/references/codebase-memory-routing.md +57 -0
- package/plugins/codex-workflow-gateway/references/protocol.md +42 -445
- package/plugins/codex-workflow-gateway/scripts/chat-dispatch.mjs +7 -1
- package/plugins/codex-workflow-gateway/scripts/chat-model-policy.mjs +10 -8
- package/plugins/codex-workflow-gateway/scripts/chat-registry.mjs +23 -5
- package/plugins/codex-workflow-gateway/skills/codex-workflow-gateway/SKILL.md +66 -757
- package/references/state-machine.md +4 -4
- package/roles/technical-planner.md +1 -1
- package/schemas/corrective-decision-event.schema.json +3 -1
- package/schemas/project-knowledge-map.schema.json +3 -1
- package/schemas/remediation-event.schema.json +10 -1
- package/schemas/task.schema.json +46 -2
- package/schemas/transition-payloads.schema.json +16 -1
- package/src/alpha6/adoption.ts +1123 -0
- package/src/alpha6/captured-check-evidence.ts +132 -0
- package/src/alpha6/check-support-anchor.ts +128 -0
- package/src/alpha6/component-owner.ts +168 -0
- package/src/alpha6/corrective-decision-boundary.ts +39 -0
- package/src/alpha6/downstream-proof.ts +520 -0
- package/src/alpha6/failed-step-planning-recovery.ts +88 -0
- package/src/alpha6/handoff.ts +1443 -0
- package/src/alpha6/journal.ts +473 -0
- package/src/alpha6/literal-test-invocation.ts +84 -0
- package/src/alpha6/mechanical-feasibility.ts +488 -0
- package/src/alpha6/milestone.ts +2192 -0
- package/src/alpha6/navigation-dirty-carryover.ts +154 -0
- package/src/alpha6/npm-check-contract.ts +47 -0
- package/src/alpha6/plan-integrity.ts +298 -0
- package/src/alpha6/plan-risk.ts +1480 -0
- package/src/alpha6/preexecution-replan.ts +187 -0
- package/src/alpha6/remediation-cause.ts +98 -0
- package/src/alpha6/remediation.ts +2438 -0
- package/src/alpha6/review.ts +1198 -0
- package/src/alpha6/root-cause-replan-carryover.ts +491 -0
- package/src/alpha6/store-sidecars.ts +335 -0
- package/src/alpha7/autonomy.ts +411 -0
- package/src/alpha7/corrective-recovery.ts +1332 -0
- package/src/artifacts.ts +130 -0
- package/src/beta1/project-transaction.ts +355 -0
- package/src/change-explanation.ts +153 -0
- package/src/checks/runner.ts +245 -0
- package/src/checks/task-sync.ts +41 -0
- package/src/checks/worker.ts +149 -0
- package/src/cli-actions.ts +107 -0
- package/src/cli.ts +1457 -0
- package/src/contracts.ts +1494 -0
- package/src/credential-output.ts +89 -0
- package/src/credential-transport.ts +215 -0
- package/src/delegation.ts +190 -0
- package/src/dependency-provenance.ts +472 -0
- package/src/diagnostics.ts +93 -0
- package/src/domain/base-sync-conflict.ts +35 -0
- package/src/domain/completed-step-carryover.ts +76 -0
- package/src/domain/discovery.ts +27 -0
- package/src/domain/plan-semantics.ts +58 -0
- package/src/domain/step-start-admission.ts +10 -0
- package/src/domain/validation.ts +177 -0
- package/src/domain/virgin-registration.ts +46 -0
- package/src/errors.ts +24 -0
- package/src/fs-utils.ts +61 -0
- package/src/gateway-handshake.ts +95 -0
- package/src/git.ts +183 -0
- package/src/graph.ts +342 -0
- package/src/historical-step-provenance.ts +136 -0
- package/src/index.ts +23 -0
- package/src/lifecycle/canonical-hash.ts +28 -0
- package/src/lifecycle/catalog.ts +202 -0
- package/src/lifecycle/compiler-inspection.ts +29 -0
- package/src/lifecycle/core-static-readiness.ts +132 -0
- package/src/lifecycle/corrective-replan-authority.ts +136 -0
- package/src/lifecycle/corrective-replan-binding-manifest.ts +51 -0
- package/src/lifecycle/corrective-replan-credential-core.ts +408 -0
- package/src/lifecycle/corrective-replan-credential-schema.ts +54 -0
- package/src/lifecycle/corrective-replan-credentials.ts +48 -0
- package/src/lifecycle/corrective-replan.ts +843 -0
- package/src/lifecycle/evaluator.ts +48 -0
- package/src/lifecycle/fingerprint.ts +488 -0
- package/src/lifecycle/immutable.ts +8 -0
- package/src/lifecycle/implementation-table.ts +118 -0
- package/src/lifecycle/index.ts +8 -0
- package/src/lifecycle/schema-artifact.ts +263 -0
- package/src/lifecycle/semantic-registry.ts +572 -0
- package/src/lifecycle/types.ts +838 -0
- package/src/memory.ts +273 -0
- package/src/migration.ts +161 -0
- package/src/navigation-actions.ts +70 -0
- package/src/navigation-update-artifact.ts +198 -0
- package/src/navigation-update.ts +121 -0
- package/src/observation.ts +225 -0
- package/src/observed-routes.ts +660 -0
- package/src/operational-contract.ts +125 -0
- package/src/pending-review-update.ts +175 -0
- package/src/repository.ts +99 -0
- package/src/reviewer.ts +1879 -0
- package/src/runtime.ts +6 -0
- package/src/state/corrective-replan-executor.ts +818 -0
- package/src/state/corrective-replan-public-schema.ts +83 -0
- package/src/state/corrective-replan-public.ts +908 -0
- package/src/state/corrective-replan-transaction.ts +949 -0
- package/src/state/corrective-yield-executor.ts +327 -0
- package/src/state/corrective-yield-transaction.ts +730 -0
- package/src/state/lock.ts +902 -0
- package/src/state/store.ts +567 -0
- package/src/transition-core.ts +330 -0
- package/src/ulid.ts +24 -0
- package/src/version.ts +2 -0
- package/src/workflow-blocker-route.ts +109 -0
- package/src/workflow.ts +10172 -0
- package/docs/alpha7.1-implementation-brief.md +0 -268
- package/docs/alpha7.2-corrective-context-refresh-brief.md +0 -484
- package/docs/alpha7.2.1-remediation-recovery-brief.md +0 -86
- package/docs/beta1-stabilization-brief.md +0 -165
- package/docs/beta11-plan-integrity-recovery-brief.md +0 -38
- package/docs/beta13.2-signal-review-recovery.md +0 -38
- package/docs/beta2-initial-assembly-navigation-brief.md +0 -616
- package/docs/change-model.md +0 -118
- package/docs/delegated-approval.md +0 -254
- package/docs/lifecycle/state-machine-stabilization.md +0 -641
- package/docs/pdf/README.md +0 -24
- package/docs/pdf/codex-workflow-v2-architecture-ru.pdf +0 -0
- package/docs/pdf/codex-workflow-v2-chat-only-guide-ru.pdf +0 -0
- package/docs/pdf/codex-workflow-v2-technical-reference-ru.pdf +0 -0
- package/docs/pdf/requirements.txt +0 -1
- package/docs/pdf/sources/codex-workflow-v2-architecture-ru.md +0 -478
- package/docs/pdf/sources/codex-workflow-v2-chat-only-guide-ru.md +0 -506
- package/docs/pdf/sources/codex-workflow-v2-technical-reference-ru.md +0 -778
- package/docs/pending-review-update.md +0 -15
- package/docs/problem-briefs/01-pre-implementation-integrity.md +0 -482
- package/docs/problem-briefs/02-minimal-step-integrity.md +0 -411
- package/docs/problem-briefs/03-minimal-agent-context-integrity.md +0 -358
- package/docs/problem-briefs/04-task-dependency-and-structural-replacement-integrity.md +0 -573
- package/docs/problem-briefs/BRIEF-TEMPLATE.md +0 -56
- package/docs/problem-briefs/README.md +0 -120
- package/docs/problem-briefs/evidence/p01-mechanical-feasibility-corpus.md +0 -90
- package/docs/problem-briefs/evidence/signal-v4-pre-m3-replay.md +0 -246
- package/docs/split-required-recovery.md +0 -47
- package/docs/stable-release-defect-register.md +0 -730
- package/docs/validation-report.md +0 -182
- package/scripts/generate-pdf-docs.py +0 -524
- package/scripts/run-pdf-docs.mjs +0 -62
|
@@ -1,524 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
"""Generate the tracked Russian Workflow V2 PDF documentation.
|
|
3
|
-
|
|
4
|
-
The Markdown sources under docs/pdf/sources are the reviewable authority for the
|
|
5
|
-
three human-facing PDF artifacts. The generator is deterministic so release
|
|
6
|
-
validation can compare regenerated bytes with the committed files.
|
|
7
|
-
"""
|
|
8
|
-
|
|
9
|
-
from __future__ import annotations
|
|
10
|
-
|
|
11
|
-
import argparse
|
|
12
|
-
import hashlib
|
|
13
|
-
import html
|
|
14
|
-
import json
|
|
15
|
-
import re
|
|
16
|
-
import sys
|
|
17
|
-
import tempfile
|
|
18
|
-
from pathlib import Path
|
|
19
|
-
|
|
20
|
-
from reportlab.lib import colors
|
|
21
|
-
from reportlab.lib.enums import TA_CENTER, TA_LEFT
|
|
22
|
-
from reportlab.lib.pagesizes import A4
|
|
23
|
-
from reportlab.lib.styles import ParagraphStyle, getSampleStyleSheet
|
|
24
|
-
from reportlab.lib.units import mm
|
|
25
|
-
from reportlab.pdfbase import pdfmetrics
|
|
26
|
-
from reportlab.pdfbase.ttfonts import TTFont
|
|
27
|
-
from reportlab.pdfgen import canvas
|
|
28
|
-
from reportlab.platypus import (
|
|
29
|
-
KeepTogether,
|
|
30
|
-
ListFlowable,
|
|
31
|
-
ListItem,
|
|
32
|
-
LongTable,
|
|
33
|
-
PageBreak,
|
|
34
|
-
Paragraph,
|
|
35
|
-
SimpleDocTemplate,
|
|
36
|
-
Spacer,
|
|
37
|
-
Table,
|
|
38
|
-
TableStyle,
|
|
39
|
-
)
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
ROOT = Path(__file__).resolve().parents[1]
|
|
43
|
-
SOURCE_DIR = ROOT / "docs" / "pdf" / "sources"
|
|
44
|
-
OUTPUT_DIR = ROOT / "docs" / "pdf"
|
|
45
|
-
PACKAGE = json.loads((ROOT / "package.json").read_text(encoding="utf-8"))
|
|
46
|
-
PACKAGE_VERSION = PACKAGE["version"]
|
|
47
|
-
RELEASE_LABEL = PACKAGE_VERSION.rsplit("-", 1)[-1]
|
|
48
|
-
|
|
49
|
-
DOCUMENTS = (
|
|
50
|
-
("codex-workflow-v2-architecture-ru.md", "codex-workflow-v2-architecture-ru.pdf"),
|
|
51
|
-
("codex-workflow-v2-chat-only-guide-ru.md", "codex-workflow-v2-chat-only-guide-ru.pdf"),
|
|
52
|
-
("codex-workflow-v2-technical-reference-ru.md", "codex-workflow-v2-technical-reference-ru.pdf"),
|
|
53
|
-
)
|
|
54
|
-
|
|
55
|
-
PAGE_WIDTH, PAGE_HEIGHT = A4
|
|
56
|
-
LEFT = 22 * mm
|
|
57
|
-
RIGHT = 20 * mm
|
|
58
|
-
TOP = 22 * mm
|
|
59
|
-
BOTTOM = 18 * mm
|
|
60
|
-
CONTENT_WIDTH = PAGE_WIDTH - LEFT - RIGHT
|
|
61
|
-
|
|
62
|
-
NAVY = colors.HexColor("#14213D")
|
|
63
|
-
BLUE = colors.HexColor("#2563EB")
|
|
64
|
-
PALE_BLUE = colors.HexColor("#EFF6FF")
|
|
65
|
-
PALE_GREEN = colors.HexColor("#ECFDF5")
|
|
66
|
-
PALE_ORANGE = colors.HexColor("#FFF7ED")
|
|
67
|
-
PALE_GRAY = colors.HexColor("#F8FAFC")
|
|
68
|
-
MID_GRAY = colors.HexColor("#CBD5E1")
|
|
69
|
-
TEXT = colors.HexColor("#172033")
|
|
70
|
-
MUTED = colors.HexColor("#64748B")
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
def first_existing(candidates: tuple[str, ...]) -> str:
|
|
74
|
-
for candidate in candidates:
|
|
75
|
-
if Path(candidate).is_file():
|
|
76
|
-
return candidate
|
|
77
|
-
raise RuntimeError(f"No usable font found in: {', '.join(candidates)}")
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
def register_fonts() -> None:
|
|
81
|
-
regular = first_existing((
|
|
82
|
-
"/System/Library/Fonts/Supplemental/Arial.ttf",
|
|
83
|
-
"/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf",
|
|
84
|
-
"/usr/share/fonts/dejavu/DejaVuSans.ttf",
|
|
85
|
-
))
|
|
86
|
-
bold = first_existing((
|
|
87
|
-
"/System/Library/Fonts/Supplemental/Arial Bold.ttf",
|
|
88
|
-
"/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf",
|
|
89
|
-
"/usr/share/fonts/dejavu/DejaVuSans-Bold.ttf",
|
|
90
|
-
))
|
|
91
|
-
italic = first_existing((
|
|
92
|
-
"/System/Library/Fonts/Supplemental/Arial Italic.ttf",
|
|
93
|
-
"/usr/share/fonts/truetype/dejavu/DejaVuSans-Oblique.ttf",
|
|
94
|
-
"/usr/share/fonts/dejavu/DejaVuSans-Oblique.ttf",
|
|
95
|
-
))
|
|
96
|
-
bold_italic = first_existing((
|
|
97
|
-
"/System/Library/Fonts/Supplemental/Arial Bold Italic.ttf",
|
|
98
|
-
"/usr/share/fonts/truetype/dejavu/DejaVuSans-BoldOblique.ttf",
|
|
99
|
-
"/usr/share/fonts/dejavu/DejaVuSans-BoldOblique.ttf",
|
|
100
|
-
))
|
|
101
|
-
pdfmetrics.registerFont(TTFont("WorkflowBody", regular))
|
|
102
|
-
pdfmetrics.registerFont(TTFont("WorkflowBold", bold))
|
|
103
|
-
pdfmetrics.registerFont(TTFont("WorkflowItalic", italic))
|
|
104
|
-
pdfmetrics.registerFont(TTFont("WorkflowBoldItalic", bold_italic))
|
|
105
|
-
pdfmetrics.registerFontFamily(
|
|
106
|
-
"WorkflowBody",
|
|
107
|
-
normal="WorkflowBody",
|
|
108
|
-
bold="WorkflowBold",
|
|
109
|
-
italic="WorkflowItalic",
|
|
110
|
-
boldItalic="WorkflowBoldItalic",
|
|
111
|
-
)
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
class InvariantCanvas(canvas.Canvas):
|
|
115
|
-
def __init__(self, *args, **kwargs):
|
|
116
|
-
kwargs["invariant"] = 1
|
|
117
|
-
super().__init__(*args, **kwargs)
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
def styles() -> dict[str, ParagraphStyle]:
|
|
121
|
-
base = getSampleStyleSheet()
|
|
122
|
-
return {
|
|
123
|
-
"title": ParagraphStyle(
|
|
124
|
-
"Title",
|
|
125
|
-
parent=base["Title"],
|
|
126
|
-
fontName="WorkflowBold",
|
|
127
|
-
fontSize=25,
|
|
128
|
-
leading=30,
|
|
129
|
-
textColor=NAVY,
|
|
130
|
-
alignment=TA_LEFT,
|
|
131
|
-
spaceAfter=10,
|
|
132
|
-
),
|
|
133
|
-
"subtitle": ParagraphStyle(
|
|
134
|
-
"Subtitle",
|
|
135
|
-
parent=base["Normal"],
|
|
136
|
-
fontName="WorkflowBody",
|
|
137
|
-
fontSize=12,
|
|
138
|
-
leading=17,
|
|
139
|
-
textColor=MUTED,
|
|
140
|
-
spaceAfter=18,
|
|
141
|
-
),
|
|
142
|
-
"part": ParagraphStyle(
|
|
143
|
-
"Part",
|
|
144
|
-
parent=base["Normal"],
|
|
145
|
-
fontName="WorkflowBold",
|
|
146
|
-
fontSize=8,
|
|
147
|
-
leading=10,
|
|
148
|
-
textColor=BLUE,
|
|
149
|
-
spaceAfter=10,
|
|
150
|
-
),
|
|
151
|
-
"h1": ParagraphStyle(
|
|
152
|
-
"H1",
|
|
153
|
-
parent=base["Heading1"],
|
|
154
|
-
fontName="WorkflowBold",
|
|
155
|
-
fontSize=18,
|
|
156
|
-
leading=22,
|
|
157
|
-
textColor=NAVY,
|
|
158
|
-
spaceBefore=10,
|
|
159
|
-
spaceAfter=8,
|
|
160
|
-
keepWithNext=True,
|
|
161
|
-
),
|
|
162
|
-
"h2": ParagraphStyle(
|
|
163
|
-
"H2",
|
|
164
|
-
parent=base["Heading2"],
|
|
165
|
-
fontName="WorkflowBold",
|
|
166
|
-
fontSize=14,
|
|
167
|
-
leading=18,
|
|
168
|
-
textColor=BLUE,
|
|
169
|
-
spaceBefore=8,
|
|
170
|
-
spaceAfter=6,
|
|
171
|
-
keepWithNext=True,
|
|
172
|
-
),
|
|
173
|
-
"h3": ParagraphStyle(
|
|
174
|
-
"H3",
|
|
175
|
-
parent=base["Heading3"],
|
|
176
|
-
fontName="WorkflowBold",
|
|
177
|
-
fontSize=11,
|
|
178
|
-
leading=14,
|
|
179
|
-
textColor=NAVY,
|
|
180
|
-
spaceBefore=6,
|
|
181
|
-
spaceAfter=4,
|
|
182
|
-
keepWithNext=True,
|
|
183
|
-
),
|
|
184
|
-
"body": ParagraphStyle(
|
|
185
|
-
"Body",
|
|
186
|
-
parent=base["BodyText"],
|
|
187
|
-
fontName="WorkflowBody",
|
|
188
|
-
fontSize=9.1,
|
|
189
|
-
leading=13.2,
|
|
190
|
-
textColor=TEXT,
|
|
191
|
-
spaceAfter=5,
|
|
192
|
-
allowWidows=0,
|
|
193
|
-
allowOrphans=0,
|
|
194
|
-
),
|
|
195
|
-
"small": ParagraphStyle(
|
|
196
|
-
"Small",
|
|
197
|
-
parent=base["BodyText"],
|
|
198
|
-
fontName="WorkflowBody",
|
|
199
|
-
fontSize=7.7,
|
|
200
|
-
leading=10.3,
|
|
201
|
-
textColor=TEXT,
|
|
202
|
-
),
|
|
203
|
-
"table_header": ParagraphStyle(
|
|
204
|
-
"TableHeader",
|
|
205
|
-
parent=base["BodyText"],
|
|
206
|
-
fontName="WorkflowBold",
|
|
207
|
-
fontSize=7.7,
|
|
208
|
-
leading=10.3,
|
|
209
|
-
textColor=colors.white,
|
|
210
|
-
),
|
|
211
|
-
"code": ParagraphStyle(
|
|
212
|
-
"Code",
|
|
213
|
-
parent=base["BodyText"],
|
|
214
|
-
fontName="WorkflowBody",
|
|
215
|
-
fontSize=7.6,
|
|
216
|
-
leading=10.1,
|
|
217
|
-
textColor=colors.HexColor("#E2E8F0"),
|
|
218
|
-
backColor=NAVY,
|
|
219
|
-
borderPadding=0,
|
|
220
|
-
wordWrap="CJK",
|
|
221
|
-
),
|
|
222
|
-
"callout": ParagraphStyle(
|
|
223
|
-
"Callout",
|
|
224
|
-
parent=base["BodyText"],
|
|
225
|
-
fontName="WorkflowBody",
|
|
226
|
-
fontSize=8.5,
|
|
227
|
-
leading=12,
|
|
228
|
-
textColor=TEXT,
|
|
229
|
-
),
|
|
230
|
-
"footer": ParagraphStyle(
|
|
231
|
-
"Footer",
|
|
232
|
-
parent=base["Normal"],
|
|
233
|
-
fontName="WorkflowBody",
|
|
234
|
-
fontSize=7,
|
|
235
|
-
textColor=MUTED,
|
|
236
|
-
alignment=TA_CENTER,
|
|
237
|
-
),
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
def inline_markup(value: str) -> str:
|
|
242
|
-
escaped = html.escape(value.strip())
|
|
243
|
-
escaped = re.sub(r"`([^`]+)`", r'<font name="WorkflowBold">\1</font>', escaped)
|
|
244
|
-
escaped = re.sub(r"\*\*([^*]+)\*\*", r"<b>\1</b>", escaped)
|
|
245
|
-
escaped = re.sub(r"\*([^*]+)\*", r"<i>\1</i>", escaped)
|
|
246
|
-
return escaped
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
def parse_source(path: Path) -> tuple[dict[str, str], list[str]]:
|
|
250
|
-
lines = path.read_text(encoding="utf-8").splitlines()
|
|
251
|
-
if not lines or lines[0].strip() != "---":
|
|
252
|
-
raise RuntimeError(f"{path} must start with frontmatter")
|
|
253
|
-
metadata: dict[str, str] = {}
|
|
254
|
-
index = 1
|
|
255
|
-
while index < len(lines) and lines[index].strip() != "---":
|
|
256
|
-
line = lines[index]
|
|
257
|
-
if ":" not in line:
|
|
258
|
-
raise RuntimeError(f"Invalid frontmatter line in {path}: {line}")
|
|
259
|
-
key, value = line.split(":", 1)
|
|
260
|
-
metadata[key.strip()] = value.strip().strip('"')
|
|
261
|
-
index += 1
|
|
262
|
-
if index >= len(lines):
|
|
263
|
-
raise RuntimeError(f"Unclosed frontmatter in {path}")
|
|
264
|
-
return metadata, lines[index + 1 :]
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
def table_widths(column_count: int) -> list[float]:
|
|
268
|
-
if column_count == 2:
|
|
269
|
-
return [CONTENT_WIDTH * 0.34, CONTENT_WIDTH * 0.66]
|
|
270
|
-
if column_count == 3:
|
|
271
|
-
return [CONTENT_WIDTH * 0.26, CONTENT_WIDTH * 0.34, CONTENT_WIDTH * 0.40]
|
|
272
|
-
return [CONTENT_WIDTH / column_count] * column_count
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
def markdown_story(lines: list[str], style: dict[str, ParagraphStyle]) -> list:
|
|
276
|
-
story: list = []
|
|
277
|
-
index = 0
|
|
278
|
-
while index < len(lines):
|
|
279
|
-
raw = lines[index]
|
|
280
|
-
stripped = raw.strip()
|
|
281
|
-
if not stripped:
|
|
282
|
-
story.append(Spacer(1, 2.5))
|
|
283
|
-
index += 1
|
|
284
|
-
continue
|
|
285
|
-
if stripped == "<!-- pagebreak -->":
|
|
286
|
-
story.append(PageBreak())
|
|
287
|
-
index += 1
|
|
288
|
-
continue
|
|
289
|
-
if stripped.startswith("```"):
|
|
290
|
-
code_lines: list[str] = []
|
|
291
|
-
index += 1
|
|
292
|
-
while index < len(lines) and not lines[index].strip().startswith("```"):
|
|
293
|
-
code_lines.append(lines[index].rstrip())
|
|
294
|
-
index += 1
|
|
295
|
-
if index >= len(lines):
|
|
296
|
-
raise RuntimeError("Unclosed code block")
|
|
297
|
-
code_html = "<br/>".join(html.escape(line or " ") for line in code_lines)
|
|
298
|
-
block = Table([[Paragraph(code_html, style["code"])]], colWidths=[CONTENT_WIDTH])
|
|
299
|
-
block.setStyle(TableStyle([
|
|
300
|
-
("BACKGROUND", (0, 0), (-1, -1), NAVY),
|
|
301
|
-
("BOX", (0, 0), (-1, -1), 0.8, BLUE),
|
|
302
|
-
("LEFTPADDING", (0, 0), (-1, -1), 10),
|
|
303
|
-
("RIGHTPADDING", (0, 0), (-1, -1), 10),
|
|
304
|
-
("TOPPADDING", (0, 0), (-1, -1), 8),
|
|
305
|
-
("BOTTOMPADDING", (0, 0), (-1, -1), 8),
|
|
306
|
-
]))
|
|
307
|
-
story.extend([block, Spacer(1, 6)])
|
|
308
|
-
index += 1
|
|
309
|
-
continue
|
|
310
|
-
heading = re.match(r"^(#{1,3})\s+(.+)$", stripped)
|
|
311
|
-
if heading:
|
|
312
|
-
level = len(heading.group(1))
|
|
313
|
-
story.append(Paragraph(inline_markup(heading.group(2)), style[f"h{level}"]))
|
|
314
|
-
index += 1
|
|
315
|
-
continue
|
|
316
|
-
if stripped.startswith("|"):
|
|
317
|
-
table_lines: list[str] = []
|
|
318
|
-
while index < len(lines) and lines[index].strip().startswith("|"):
|
|
319
|
-
table_lines.append(lines[index].strip())
|
|
320
|
-
index += 1
|
|
321
|
-
rows = [[cell.strip() for cell in line.strip("|").split("|")] for line in table_lines]
|
|
322
|
-
if len(rows) >= 2 and all(re.fullmatch(r":?-{3,}:?", cell) for cell in rows[1]):
|
|
323
|
-
rows.pop(1)
|
|
324
|
-
column_count = max(len(row) for row in rows)
|
|
325
|
-
normalized = [row + [""] * (column_count - len(row)) for row in rows]
|
|
326
|
-
rendered = [
|
|
327
|
-
[
|
|
328
|
-
Paragraph(inline_markup(cell), style["table_header"] if row_index == 0 else style["small"])
|
|
329
|
-
for cell in row
|
|
330
|
-
]
|
|
331
|
-
for row_index, row in enumerate(normalized)
|
|
332
|
-
]
|
|
333
|
-
table = LongTable(rendered, colWidths=table_widths(column_count), repeatRows=1)
|
|
334
|
-
table.setStyle(TableStyle([
|
|
335
|
-
("BACKGROUND", (0, 0), (-1, 0), NAVY),
|
|
336
|
-
("TEXTCOLOR", (0, 0), (-1, 0), colors.white),
|
|
337
|
-
("FONTNAME", (0, 0), (-1, 0), "WorkflowBold"),
|
|
338
|
-
("BACKGROUND", (0, 1), (-1, -1), PALE_GRAY),
|
|
339
|
-
("GRID", (0, 0), (-1, -1), 0.45, MID_GRAY),
|
|
340
|
-
("VALIGN", (0, 0), (-1, -1), "TOP"),
|
|
341
|
-
("LEFTPADDING", (0, 0), (-1, -1), 6),
|
|
342
|
-
("RIGHTPADDING", (0, 0), (-1, -1), 6),
|
|
343
|
-
("TOPPADDING", (0, 0), (-1, -1), 5),
|
|
344
|
-
("BOTTOMPADDING", (0, 0), (-1, -1), 5),
|
|
345
|
-
]))
|
|
346
|
-
story.extend([table, Spacer(1, 6)])
|
|
347
|
-
continue
|
|
348
|
-
if stripped.startswith(">"):
|
|
349
|
-
quote_lines: list[str] = []
|
|
350
|
-
while index < len(lines) and lines[index].strip().startswith(">"):
|
|
351
|
-
quote_lines.append(lines[index].strip()[1:].strip())
|
|
352
|
-
index += 1
|
|
353
|
-
value = " ".join(quote_lines)
|
|
354
|
-
color = PALE_ORANGE if "Важно" in value or "Стоп" in value else PALE_BLUE
|
|
355
|
-
callout = Table([[Paragraph(inline_markup(value), style["callout"])]], colWidths=[CONTENT_WIDTH])
|
|
356
|
-
callout.setStyle(TableStyle([
|
|
357
|
-
("BACKGROUND", (0, 0), (-1, -1), color),
|
|
358
|
-
("BOX", (0, 0), (-1, -1), 0.8, BLUE),
|
|
359
|
-
("LINEBEFORE", (0, 0), (0, -1), 3, BLUE),
|
|
360
|
-
("LEFTPADDING", (0, 0), (-1, -1), 10),
|
|
361
|
-
("RIGHTPADDING", (0, 0), (-1, -1), 10),
|
|
362
|
-
("TOPPADDING", (0, 0), (-1, -1), 7),
|
|
363
|
-
("BOTTOMPADDING", (0, 0), (-1, -1), 7),
|
|
364
|
-
]))
|
|
365
|
-
story.extend([callout, Spacer(1, 6)])
|
|
366
|
-
continue
|
|
367
|
-
if re.match(r"^-\s+", stripped):
|
|
368
|
-
items: list[ListItem] = []
|
|
369
|
-
while index < len(lines) and re.match(r"^-\s+", lines[index].strip()):
|
|
370
|
-
value = re.sub(r"^-\s+", "", lines[index].strip())
|
|
371
|
-
items.append(ListItem(Paragraph(inline_markup(value), style["body"]), leftIndent=10))
|
|
372
|
-
index += 1
|
|
373
|
-
story.append(ListFlowable(items, bulletType="bullet", leftIndent=16, bulletColor=BLUE))
|
|
374
|
-
story.append(Spacer(1, 3))
|
|
375
|
-
continue
|
|
376
|
-
if re.match(r"^\d+\.\s+", stripped):
|
|
377
|
-
items = []
|
|
378
|
-
while index < len(lines) and re.match(r"^\d+\.\s+", lines[index].strip()):
|
|
379
|
-
value = re.sub(r"^\d+\.\s+", "", lines[index].strip())
|
|
380
|
-
items.append(ListItem(Paragraph(inline_markup(value), style["body"]), leftIndent=12))
|
|
381
|
-
index += 1
|
|
382
|
-
story.append(ListFlowable(items, bulletType="1", leftIndent=18, bulletColor=BLUE))
|
|
383
|
-
story.append(Spacer(1, 3))
|
|
384
|
-
continue
|
|
385
|
-
|
|
386
|
-
paragraph_lines = [stripped]
|
|
387
|
-
index += 1
|
|
388
|
-
while index < len(lines):
|
|
389
|
-
candidate = lines[index].strip()
|
|
390
|
-
if (
|
|
391
|
-
not candidate
|
|
392
|
-
or candidate == "<!-- pagebreak -->"
|
|
393
|
-
or candidate.startswith(("#", "|", ">", "```", "- "))
|
|
394
|
-
or re.match(r"^\d+\.\s+", candidate)
|
|
395
|
-
):
|
|
396
|
-
break
|
|
397
|
-
paragraph_lines.append(candidate)
|
|
398
|
-
index += 1
|
|
399
|
-
story.append(Paragraph(inline_markup(" ".join(paragraph_lines)), style["body"]))
|
|
400
|
-
return story
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
def draw_page(canvas_obj: canvas.Canvas, doc: SimpleDocTemplate, title: str) -> None:
|
|
404
|
-
canvas_obj.saveState()
|
|
405
|
-
canvas_obj.setStrokeColor(MID_GRAY)
|
|
406
|
-
canvas_obj.setLineWidth(0.35)
|
|
407
|
-
canvas_obj.line(LEFT, PAGE_HEIGHT - 13 * mm, PAGE_WIDTH - RIGHT, PAGE_HEIGHT - 13 * mm)
|
|
408
|
-
canvas_obj.setFont("WorkflowBody", 7)
|
|
409
|
-
canvas_obj.setFillColor(MUTED)
|
|
410
|
-
canvas_obj.drawString(LEFT, PAGE_HEIGHT - 10 * mm, title[:74])
|
|
411
|
-
canvas_obj.drawRightString(PAGE_WIDTH - RIGHT, PAGE_HEIGHT - 10 * mm, f"{RELEASE_LABEL} | {PACKAGE_VERSION}")
|
|
412
|
-
canvas_obj.line(LEFT, 11 * mm, PAGE_WIDTH - RIGHT, 11 * mm)
|
|
413
|
-
canvas_obj.drawString(LEFT, 7 * mm, "Codex Workflow V2")
|
|
414
|
-
canvas_obj.drawRightString(PAGE_WIDTH - RIGHT, 7 * mm, f"Страница {doc.page}")
|
|
415
|
-
canvas_obj.restoreState()
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
def build_pdf(source_path: Path, output_path: Path) -> None:
|
|
419
|
-
metadata, lines = parse_source(source_path)
|
|
420
|
-
required = ("title", "subtitle", "part", "document_version", "date", "subject")
|
|
421
|
-
missing = [key for key in required if key not in metadata]
|
|
422
|
-
if missing:
|
|
423
|
-
raise RuntimeError(f"Missing metadata in {source_path}: {', '.join(missing)}")
|
|
424
|
-
style = styles()
|
|
425
|
-
output_path.parent.mkdir(parents=True, exist_ok=True)
|
|
426
|
-
doc = SimpleDocTemplate(
|
|
427
|
-
str(output_path),
|
|
428
|
-
pagesize=A4,
|
|
429
|
-
leftMargin=LEFT,
|
|
430
|
-
rightMargin=RIGHT,
|
|
431
|
-
topMargin=TOP,
|
|
432
|
-
bottomMargin=BOTTOM,
|
|
433
|
-
title=metadata["title"],
|
|
434
|
-
author="Codex Workflow Maintainers",
|
|
435
|
-
subject=metadata["subject"],
|
|
436
|
-
creator="codex-workflow-v2 deterministic PDF generator",
|
|
437
|
-
pageCompression=1,
|
|
438
|
-
)
|
|
439
|
-
title_block = [
|
|
440
|
-
Spacer(1, 28 * mm),
|
|
441
|
-
Paragraph(inline_markup(metadata["part"].upper()), style["part"]),
|
|
442
|
-
Paragraph(inline_markup(metadata["title"]), style["title"]),
|
|
443
|
-
Paragraph(inline_markup(metadata["subtitle"]), style["subtitle"]),
|
|
444
|
-
Spacer(1, 8 * mm),
|
|
445
|
-
Table(
|
|
446
|
-
[[
|
|
447
|
-
Paragraph(
|
|
448
|
-
inline_markup(
|
|
449
|
-
f"Документ {metadata['document_version']} | Пакет {PACKAGE_VERSION} | {metadata['date']}"
|
|
450
|
-
),
|
|
451
|
-
style["callout"],
|
|
452
|
-
)
|
|
453
|
-
]],
|
|
454
|
-
colWidths=[CONTENT_WIDTH],
|
|
455
|
-
style=TableStyle([
|
|
456
|
-
("BACKGROUND", (0, 0), (-1, -1), PALE_BLUE),
|
|
457
|
-
("BOX", (0, 0), (-1, -1), 0.8, BLUE),
|
|
458
|
-
("LEFTPADDING", (0, 0), (-1, -1), 10),
|
|
459
|
-
("RIGHTPADDING", (0, 0), (-1, -1), 10),
|
|
460
|
-
("TOPPADDING", (0, 0), (-1, -1), 8),
|
|
461
|
-
("BOTTOMPADDING", (0, 0), (-1, -1), 8),
|
|
462
|
-
]),
|
|
463
|
-
),
|
|
464
|
-
Spacer(1, 55 * mm),
|
|
465
|
-
Paragraph(
|
|
466
|
-
inline_markup("Целевая среда: Codex App, один пользователь, одна машина, exact project-local package."),
|
|
467
|
-
style["small"],
|
|
468
|
-
),
|
|
469
|
-
PageBreak(),
|
|
470
|
-
]
|
|
471
|
-
story = title_block + markdown_story(lines, style)
|
|
472
|
-
doc.build(
|
|
473
|
-
story,
|
|
474
|
-
onFirstPage=lambda canv, current_doc: draw_page(canv, current_doc, metadata["title"]),
|
|
475
|
-
onLaterPages=lambda canv, current_doc: draw_page(canv, current_doc, metadata["title"]),
|
|
476
|
-
canvasmaker=InvariantCanvas,
|
|
477
|
-
)
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
def sha256(path: Path) -> str:
|
|
481
|
-
return hashlib.sha256(path.read_bytes()).hexdigest()
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
def generate(output_dir: Path) -> None:
|
|
485
|
-
for source_name, output_name in DOCUMENTS:
|
|
486
|
-
build_pdf(SOURCE_DIR / source_name, output_dir / output_name)
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
def check() -> int:
|
|
490
|
-
with tempfile.TemporaryDirectory(prefix="codex-workflow-pdf-") as directory:
|
|
491
|
-
candidate_dir = Path(directory)
|
|
492
|
-
generate(candidate_dir)
|
|
493
|
-
stale = []
|
|
494
|
-
for _, output_name in DOCUMENTS:
|
|
495
|
-
tracked = OUTPUT_DIR / output_name
|
|
496
|
-
candidate = candidate_dir / output_name
|
|
497
|
-
if not tracked.is_file() or tracked.read_bytes() != candidate.read_bytes():
|
|
498
|
-
stale.append({
|
|
499
|
-
"file": str(tracked.relative_to(ROOT)),
|
|
500
|
-
"trackedSha256": sha256(tracked) if tracked.is_file() else None,
|
|
501
|
-
"generatedSha256": sha256(candidate),
|
|
502
|
-
})
|
|
503
|
-
print(json.dumps({"ok": not stale, "packageVersion": PACKAGE_VERSION, "stale": stale}, ensure_ascii=False))
|
|
504
|
-
return 0 if not stale else 1
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
def main() -> int:
|
|
508
|
-
parser = argparse.ArgumentParser(description=__doc__)
|
|
509
|
-
parser.add_argument("--check", action="store_true", help="fail when tracked PDFs differ from deterministic output")
|
|
510
|
-
args = parser.parse_args()
|
|
511
|
-
register_fonts()
|
|
512
|
-
if args.check:
|
|
513
|
-
return check()
|
|
514
|
-
generate(OUTPUT_DIR)
|
|
515
|
-
print(json.dumps({
|
|
516
|
-
"ok": True,
|
|
517
|
-
"packageVersion": PACKAGE_VERSION,
|
|
518
|
-
"outputs": [str((OUTPUT_DIR / output).relative_to(ROOT)) for _, output in DOCUMENTS],
|
|
519
|
-
}, ensure_ascii=False))
|
|
520
|
-
return 0
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
if __name__ == "__main__":
|
|
524
|
-
sys.exit(main())
|
package/scripts/run-pdf-docs.mjs
DELETED
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
import { existsSync } from 'node:fs';
|
|
2
|
-
import os from 'node:os';
|
|
3
|
-
import path from 'node:path';
|
|
4
|
-
import { spawnSync } from 'node:child_process';
|
|
5
|
-
|
|
6
|
-
const repositoryRoot = path.resolve(import.meta.dirname, '..');
|
|
7
|
-
const generator = path.join(repositoryRoot, 'scripts', 'generate-pdf-docs.py');
|
|
8
|
-
const configuredPython = process.env.CODEX_WORKFLOW_PDF_PYTHON?.trim() || null;
|
|
9
|
-
const bundledPython = path.join(
|
|
10
|
-
os.homedir(),
|
|
11
|
-
'.cache',
|
|
12
|
-
'codex-runtimes',
|
|
13
|
-
'codex-primary-runtime',
|
|
14
|
-
'dependencies',
|
|
15
|
-
'python',
|
|
16
|
-
'bin',
|
|
17
|
-
'python3',
|
|
18
|
-
);
|
|
19
|
-
const candidates = [configuredPython, bundledPython, 'python3'].filter((value, index, values) =>
|
|
20
|
-
value && values.indexOf(value) === index,
|
|
21
|
-
);
|
|
22
|
-
|
|
23
|
-
let selected = null;
|
|
24
|
-
const diagnostics = [];
|
|
25
|
-
for (const candidate of candidates) {
|
|
26
|
-
if (path.isAbsolute(candidate) && !existsSync(candidate)) {
|
|
27
|
-
diagnostics.push({ candidate, reason: 'not-found' });
|
|
28
|
-
continue;
|
|
29
|
-
}
|
|
30
|
-
const probe = spawnSync(candidate, ['-c', 'import reportlab'], { encoding: 'utf8' });
|
|
31
|
-
if (probe.status === 0) {
|
|
32
|
-
selected = candidate;
|
|
33
|
-
break;
|
|
34
|
-
}
|
|
35
|
-
diagnostics.push({
|
|
36
|
-
candidate,
|
|
37
|
-
reason: probe.error?.code ?? 'reportlab-unavailable',
|
|
38
|
-
stderr: probe.stderr?.trim() || null,
|
|
39
|
-
});
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
if (!selected) {
|
|
43
|
-
process.stderr.write(`${JSON.stringify({
|
|
44
|
-
ok: false,
|
|
45
|
-
error: 'PDF_PYTHON_UNAVAILABLE',
|
|
46
|
-
required: 'Python 3 with reportlab 4.4.x',
|
|
47
|
-
diagnostics,
|
|
48
|
-
})}\n`);
|
|
49
|
-
process.exit(1);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
const result = spawnSync(selected, [generator, ...process.argv.slice(2)], {
|
|
53
|
-
cwd: repositoryRoot,
|
|
54
|
-
encoding: 'utf8',
|
|
55
|
-
});
|
|
56
|
-
if (result.stdout) process.stdout.write(result.stdout);
|
|
57
|
-
if (result.stderr) process.stderr.write(result.stderr);
|
|
58
|
-
if (result.error) {
|
|
59
|
-
process.stderr.write(`${JSON.stringify({ ok: false, error: result.error.message })}\n`);
|
|
60
|
-
process.exit(1);
|
|
61
|
-
}
|
|
62
|
-
process.exit(result.status ?? 1);
|