aub-workspace 0.3.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/LICENSE +201 -0
- package/README.md +23 -0
- package/bin/aub-workspace.mjs +246 -0
- package/package.json +32 -0
- package/vendor/aub/apps/editor/dist/assets/_commonjs-dynamic-modules-TDtrdbi3.js +1 -0
- package/vendor/aub/apps/editor/dist/assets/angular-importer.lib-dB_jK4mR.js +32 -0
- package/vendor/aub/apps/editor/dist/assets/canvas-tools-CuYC7cA2.js +364 -0
- package/vendor/aub/apps/editor/dist/assets/design-bridge.lib-DJvaK6AX.js +1 -0
- package/vendor/aub/apps/editor/dist/assets/export-agent-prompt.lib-BsP0KNqo.js +2 -0
- package/vendor/aub/apps/editor/dist/assets/export-md.lib-DdmdeWgO.js +3 -0
- package/vendor/aub/apps/editor/dist/assets/handoff-package.lib-DDYpcEma.js +20 -0
- package/vendor/aub/apps/editor/dist/assets/implementation-report.lib-CmsSB_8s.js +1 -0
- package/vendor/aub/apps/editor/dist/assets/index-BCH-ek3h.js +2 -0
- package/vendor/aub/apps/editor/dist/assets/index-lAnc928Q.css +1 -0
- package/vendor/aub/apps/editor/dist/assets/index-vt1nM1M4.js +507 -0
- package/vendor/aub/apps/editor/dist/assets/jszip.min-CRfXyL92.js +12 -0
- package/vendor/aub/apps/editor/dist/assets/react-vendor-ByX9Pqse.js +40 -0
- package/vendor/aub/apps/editor/dist/brand/android-chrome-192x192.png +0 -0
- package/vendor/aub/apps/editor/dist/brand/android-chrome-512x512.png +0 -0
- package/vendor/aub/apps/editor/dist/brand/app-icon-1024.png +0 -0
- package/vendor/aub/apps/editor/dist/brand/app-icon-192.png +0 -0
- package/vendor/aub/apps/editor/dist/brand/app-icon-512.png +0 -0
- package/vendor/aub/apps/editor/dist/brand/apple-touch-icon.png +0 -0
- package/vendor/aub/apps/editor/dist/brand/aub-logo-mark.svg +28 -0
- package/vendor/aub/apps/editor/dist/brand/favicon-16x16.png +0 -0
- package/vendor/aub/apps/editor/dist/brand/favicon-32x32.png +0 -0
- package/vendor/aub/apps/editor/dist/brand/favicon-48x48.png +0 -0
- package/vendor/aub/apps/editor/dist/brand/favicon.ico +0 -0
- package/vendor/aub/apps/editor/dist/brand/favicon.svg +9 -0
- package/vendor/aub/apps/editor/dist/brand/maskable-icon-512.png +0 -0
- package/vendor/aub/apps/editor/dist/brand/mstile-150x150.png +0 -0
- package/vendor/aub/apps/editor/dist/brand/safari-pinned-tab.svg +8 -0
- package/vendor/aub/apps/editor/dist/browserconfig.xml +9 -0
- package/vendor/aub/apps/editor/dist/index.html +22 -0
- package/vendor/aub/apps/editor/dist/manifest.webmanifest +28 -0
- package/vendor/aub/apps/editor/dist/template-previews/admin-table.png +0 -0
- package/vendor/aub/apps/editor/dist/template-previews/booking.png +0 -0
- package/vendor/aub/apps/editor/dist/template-previews/calendar.png +0 -0
- package/vendor/aub/apps/editor/dist/template-previews/catalog.png +0 -0
- package/vendor/aub/apps/editor/dist/template-previews/chat.png +0 -0
- package/vendor/aub/apps/editor/dist/template-previews/checkout.png +0 -0
- package/vendor/aub/apps/editor/dist/template-previews/crm.png +0 -0
- package/vendor/aub/apps/editor/dist/template-previews/dashboard.png +0 -0
- package/vendor/aub/apps/editor/dist/template-previews/feed.png +0 -0
- package/vendor/aub/apps/editor/dist/template-previews/files.png +0 -0
- package/vendor/aub/apps/editor/dist/template-previews/kanban.png +0 -0
- package/vendor/aub/apps/editor/dist/template-previews/landing.png +0 -0
- package/vendor/aub/apps/editor/dist/template-previews/mail.png +0 -0
- package/vendor/aub/apps/editor/dist/template-previews/onboarding.png +0 -0
- package/vendor/aub/apps/editor/dist/template-previews/pricing.png +0 -0
- package/vendor/aub/apps/editor/dist/template-previews/product-detail.png +0 -0
- package/vendor/aub/apps/editor/dist/template-previews/settings.png +0 -0
- package/vendor/aub/apps/editor/dist/template-previews/wiki.png +0 -0
- package/vendor/aub/apps/mcp-server/dist/aub.js +15 -0
- package/vendor/aub/apps/mcp-server/dist/context.js +1 -0
- package/vendor/aub/apps/mcp-server/dist/http.js +123 -0
- package/vendor/aub/apps/mcp-server/dist/index.js +23 -0
- package/vendor/aub/apps/mcp-server/dist/repo.js +17 -0
- package/vendor/aub/apps/mcp-server/dist/schema.js +42 -0
- package/vendor/aub/apps/mcp-server/dist/server.js +80 -0
- package/vendor/aub/apps/mcp-server/dist/tools/approve-component-candidate.js +27 -0
- package/vendor/aub/apps/mcp-server/dist/tools/diff-blueprints.js +27 -0
- package/vendor/aub/apps/mcp-server/dist/tools/export-handoff.js +87 -0
- package/vendor/aub/apps/mcp-server/dist/tools/export-prompt.js +35 -0
- package/vendor/aub/apps/mcp-server/dist/tools/export-template-authoring-prompt.js +13 -0
- package/vendor/aub/apps/mcp-server/dist/tools/generate-template-from-source.js +25 -0
- package/vendor/aub/apps/mcp-server/dist/tools/get-aub-session.js +13 -0
- package/vendor/aub/apps/mcp-server/dist/tools/get-blueprint.js +28 -0
- package/vendor/aub/apps/mcp-server/dist/tools/get-project.js +45 -0
- package/vendor/aub/apps/mcp-server/dist/tools/get-workspace-status.js +10 -0
- package/vendor/aub/apps/mcp-server/dist/tools/import-design-bridge.js +62 -0
- package/vendor/aub/apps/mcp-server/dist/tools/list-blueprints.js +11 -0
- package/vendor/aub/apps/mcp-server/dist/tools/list-projects.js +11 -0
- package/vendor/aub/apps/mcp-server/dist/tools/lock-blueprint.js +33 -0
- package/vendor/aub/apps/mcp-server/dist/tools/migrate-blueprint.js +38 -0
- package/vendor/aub/apps/mcp-server/dist/tools/resolve-component.js +51 -0
- package/vendor/aub/apps/mcp-server/dist/tools/scaffold-blueprint.js +53 -0
- package/vendor/aub/apps/mcp-server/dist/tools/scan-project-ui.js +18 -0
- package/vendor/aub/apps/mcp-server/dist/tools/submit-report.js +48 -0
- package/vendor/aub/apps/mcp-server/dist/tools/update-aub-session.js +14 -0
- package/vendor/aub/apps/mcp-server/dist/tools/validate-blueprint.js +67 -0
- package/vendor/aub/apps/mcp-server/dist/tools/validate-project.js +74 -0
- package/vendor/aub/apps/mcp-server/dist/tools/write-blueprint.js +72 -0
- package/vendor/aub/apps/mcp-server/dist/workspace.js +138 -0
- package/vendor/aub/docs/agent-handoff.md +85 -0
- package/vendor/aub/docs/agent-handoff.zh-Hant.md +85 -0
- package/vendor/aub/docs/template-authoring-agent.md +86 -0
- package/vendor/aub/schema/aub-ci.schema.json +34 -0
- package/vendor/aub/schema/aub.registry.schema.json +118 -0
- package/vendor/aub/schema/design-bridge.schema.json +44 -0
- package/vendor/aub/schema/implementation-report.schema.json +93 -0
- package/vendor/aub/schema/project-types.ts +72 -0
- package/vendor/aub/schema/registry/components.json +118 -0
- package/vendor/aub/schema/types.js +13 -0
- package/vendor/aub/schema/types.ts +348 -0
- package/vendor/aub/schema/ui-blueprint-lock.schema.json +61 -0
- package/vendor/aub/schema/ui-blueprint.schema.json +1339 -0
- package/vendor/aub/schema/ui-project.schema.json +139 -0
- package/vendor/aub/scripts/agent-implementation-benchmark.lib.mjs +125 -0
- package/vendor/aub/scripts/angular-importer.lib.mjs +982 -0
- package/vendor/aub/scripts/check-editor-bundle-budget.mjs +36 -0
- package/vendor/aub/scripts/ci-verify.lib.mjs +256 -0
- package/vendor/aub/scripts/ci-verify.mjs +45 -0
- package/vendor/aub/scripts/create-authoring-kit.mjs +84 -0
- package/vendor/aub/scripts/create-implementation-report.mjs +24 -0
- package/vendor/aub/scripts/design-bridge.lib.d.mts +32 -0
- package/vendor/aub/scripts/design-bridge.lib.mjs +69 -0
- package/vendor/aub/scripts/diff-blueprint.lib.d.mts +18 -0
- package/vendor/aub/scripts/diff-blueprint.lib.mjs +148 -0
- package/vendor/aub/scripts/diff-blueprint.mjs +25 -0
- package/vendor/aub/scripts/export-agent-prompt.lib.d.mts +10 -0
- package/vendor/aub/scripts/export-agent-prompt.lib.mjs +160 -0
- package/vendor/aub/scripts/export-agent-prompt.mjs +79 -0
- package/vendor/aub/scripts/export-md.lib.d.mts +3 -0
- package/vendor/aub/scripts/export-md.lib.mjs +302 -0
- package/vendor/aub/scripts/export-md.mjs +43 -0
- package/vendor/aub/scripts/generate-registry-artifacts.lib.mjs +118 -0
- package/vendor/aub/scripts/generate-registry-artifacts.mjs +65 -0
- package/vendor/aub/scripts/generate-site-locales.mjs +545 -0
- package/vendor/aub/scripts/handoff-package.lib.d.mts +20 -0
- package/vendor/aub/scripts/handoff-package.lib.mjs +111 -0
- package/vendor/aub/scripts/implementation-report.lib.d.mts +21 -0
- package/vendor/aub/scripts/implementation-report.lib.mjs +97 -0
- package/vendor/aub/scripts/import-angular-component.mjs +72 -0
- package/vendor/aub/scripts/import-design-bridge.mjs +59 -0
- package/vendor/aub/scripts/lock-blueprint.lib.d.mts +23 -0
- package/vendor/aub/scripts/lock-blueprint.lib.mjs +58 -0
- package/vendor/aub/scripts/lock-blueprint.mjs +36 -0
- package/vendor/aub/scripts/migrate-blueprint-cli.mjs +28 -0
- package/vendor/aub/scripts/migrate-blueprint.d.mts +5 -0
- package/vendor/aub/scripts/migrate-blueprint.mjs +95 -0
- package/vendor/aub/scripts/package-workspace-cli.mjs +34 -0
- package/vendor/aub/scripts/project.lib.d.mts +44 -0
- package/vendor/aub/scripts/project.lib.mjs +175 -0
- package/vendor/aub/scripts/project.mjs +332 -0
- package/vendor/aub/scripts/registry.lib.d.mts +52 -0
- package/vendor/aub/scripts/registry.lib.mjs +222 -0
- package/vendor/aub/scripts/run-agent-implementation.mjs +423 -0
- package/vendor/aub/scripts/run-agent-readability.mjs +145 -0
- package/vendor/aub/scripts/run-ollama-prompt.mjs +30 -0
- package/vendor/aub/scripts/scaffold-blueprint.lib.d.mts +38 -0
- package/vendor/aub/scripts/scaffold-blueprint.lib.mjs +316 -0
- package/vendor/aub/scripts/scaffold-blueprint.mjs +86 -0
- package/vendor/aub/scripts/score-agent-implementation.mjs +27 -0
- package/vendor/aub/scripts/score-agent-readability.mjs +54 -0
- package/vendor/aub/scripts/sync-brand-assets.mjs +33 -0
- package/vendor/aub/scripts/validate-blueprint.lib.d.mts +14 -0
- package/vendor/aub/scripts/validate-blueprint.lib.mjs +136 -0
- package/vendor/aub/scripts/validate.mjs +128 -0
- package/vendor/aub/scripts/verify-implementation-report.mjs +36 -0
- package/vendor/aub/scripts/workspace-loop.lib.d.mts +17 -0
- package/vendor/aub/scripts/workspace-loop.lib.mjs +674 -0
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import JSZip from 'jszip';
|
|
2
|
+
|
|
3
|
+
export const HANDOFF_FORMAT_VERSION = '1.2.0';
|
|
4
|
+
export const HANDOFF_AGENT_ENTRYPOINT = 'AGENT-README.md';
|
|
5
|
+
|
|
6
|
+
export async function createHandoffArchive({
|
|
7
|
+
blueprint,
|
|
8
|
+
markdown,
|
|
9
|
+
genericPrompt,
|
|
10
|
+
codexPrompt,
|
|
11
|
+
agentGuide,
|
|
12
|
+
agentGuideZhHant,
|
|
13
|
+
reportTemplate,
|
|
14
|
+
reportSchema,
|
|
15
|
+
viewportImages,
|
|
16
|
+
extensionRegistry,
|
|
17
|
+
generatedAt = new Date().toISOString(),
|
|
18
|
+
}) {
|
|
19
|
+
const zip = new JSZip();
|
|
20
|
+
const screenId = blueprint.screen.id;
|
|
21
|
+
const hasExtensionRegistry = typeof extensionRegistry === 'string' && extensionRegistry.trim().length > 0;
|
|
22
|
+
const agentGuideText = agentGuide.replace('./agent-handoff.zh-Hant.md', './AGENT-README.zh-Hant.md');
|
|
23
|
+
const files = {
|
|
24
|
+
[HANDOFF_AGENT_ENTRYPOINT]: ensureTrailingNewline(
|
|
25
|
+
hasExtensionRegistry ? appendExtensionRegistryNote(agentGuideText) : agentGuideText
|
|
26
|
+
),
|
|
27
|
+
'AGENT-README.zh-Hant.md': ensureTrailingNewline(
|
|
28
|
+
agentGuideZhHant.replace('./agent-handoff.md', './AGENT-README.md')
|
|
29
|
+
),
|
|
30
|
+
[`${screenId}.ui.json`]: `${JSON.stringify(blueprint, null, 2)}\n`,
|
|
31
|
+
[`${screenId}.ui.md`]: ensureTrailingNewline(markdown),
|
|
32
|
+
[`${screenId}.agent.md`]: ensureTrailingNewline(genericPrompt),
|
|
33
|
+
[`${screenId}.codex.md`]: ensureTrailingNewline(codexPrompt),
|
|
34
|
+
'implementation-report.template.json': `${JSON.stringify(reportTemplate, null, 2)}\n`,
|
|
35
|
+
'implementation-report.schema.json': `${JSON.stringify(reportSchema, null, 2)}\n`,
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
if (hasExtensionRegistry) {
|
|
39
|
+
files['aub.registry.json'] = ensureTrailingNewline(extensionRegistry);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
for (const [viewportId, dataUrl] of Object.entries(viewportImages)) {
|
|
43
|
+
files[`screenshots/${viewportId}.png`] = dataUrlToBytes(dataUrl);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const manifestFiles = {};
|
|
47
|
+
for (const [path, content] of Object.entries(files)) {
|
|
48
|
+
const bytes = typeof content === 'string' ? new TextEncoder().encode(content) : content;
|
|
49
|
+
manifestFiles[path] = {
|
|
50
|
+
sha256: await sha256(bytes),
|
|
51
|
+
bytes: bytes.byteLength,
|
|
52
|
+
};
|
|
53
|
+
zip.file(path, content);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const manifest = {
|
|
57
|
+
format: 'aub-handoff',
|
|
58
|
+
format_version: HANDOFF_FORMAT_VERSION,
|
|
59
|
+
blueprint_version: blueprint.version,
|
|
60
|
+
screen_id: screenId,
|
|
61
|
+
generated_at: generatedAt,
|
|
62
|
+
agent_entrypoint: HANDOFF_AGENT_ENTRYPOINT,
|
|
63
|
+
extension_registry: hasExtensionRegistry ? 'aub.registry.json' : null,
|
|
64
|
+
viewports: blueprint.viewports,
|
|
65
|
+
files: manifestFiles,
|
|
66
|
+
};
|
|
67
|
+
zip.file('manifest.json', `${JSON.stringify(manifest, null, 2)}\n`);
|
|
68
|
+
|
|
69
|
+
return {
|
|
70
|
+
bytes: await zip.generateAsync({
|
|
71
|
+
type: 'uint8array',
|
|
72
|
+
compression: 'DEFLATE',
|
|
73
|
+
compressionOptions: { level: 6 },
|
|
74
|
+
}),
|
|
75
|
+
manifest,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function appendExtensionRegistryNote(agentGuide) {
|
|
80
|
+
const note = [
|
|
81
|
+
'',
|
|
82
|
+
'## Custom component types',
|
|
83
|
+
'',
|
|
84
|
+
'This handoff includes `aub.registry.json`, which declares namespaced extension',
|
|
85
|
+
'component types (`team:component`) used by this blueprint. Treat every node `type`',
|
|
86
|
+
'that contains a colon as a project-defined component: resolve its meaning and',
|
|
87
|
+
'container/leaf behavior from `aub.registry.json` — never guess. When an entry',
|
|
88
|
+
'declares `implementations`, reuse the matching production component, import path,',
|
|
89
|
+
'and prop mappings instead of recreating it. Validate with',
|
|
90
|
+
'`aub validate <file> --registry ./aub.registry.json`.',
|
|
91
|
+
'',
|
|
92
|
+
].join('\n');
|
|
93
|
+
return `${agentGuide.replace(/\n+$/, '')}\n${note}`;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function ensureTrailingNewline(value) {
|
|
97
|
+
return value.endsWith('\n') ? value : `${value}\n`;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function dataUrlToBytes(dataUrl) {
|
|
101
|
+
const base64 = dataUrl.split(',')[1] ?? '';
|
|
102
|
+
const binary = atob(base64);
|
|
103
|
+
return Uint8Array.from(binary, (character) => character.charCodeAt(0));
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
async function sha256(bytes) {
|
|
107
|
+
const stableBytes = new Uint8Array(bytes.byteLength);
|
|
108
|
+
stableBytes.set(bytes);
|
|
109
|
+
const buffer = await crypto.subtle.digest('SHA-256', stableBytes);
|
|
110
|
+
return Array.from(new Uint8Array(buffer), (byte) => byte.toString(16).padStart(2, '0')).join('');
|
|
111
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export type Blueprint = Record<string, any>;
|
|
2
|
+
export type ImplementationReport = Record<string, any>;
|
|
3
|
+
|
|
4
|
+
export function createImplementationReportTemplate(blueprint: Blueprint): ImplementationReport;
|
|
5
|
+
|
|
6
|
+
export interface VerifyImplementationReportResult {
|
|
7
|
+
ready: boolean;
|
|
8
|
+
errors: string[];
|
|
9
|
+
summary: {
|
|
10
|
+
nodes_total: number;
|
|
11
|
+
nodes_mapped: number;
|
|
12
|
+
acceptance_total: number;
|
|
13
|
+
acceptance_passed: number;
|
|
14
|
+
unresolved: number;
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function verifyImplementationReport(
|
|
19
|
+
blueprint: Blueprint,
|
|
20
|
+
report: ImplementationReport
|
|
21
|
+
): VerifyImplementationReportResult;
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
export function createImplementationReportTemplate(blueprint) {
|
|
2
|
+
return {
|
|
3
|
+
format: 'aub-implementation-report',
|
|
4
|
+
format_version: '1.0.0',
|
|
5
|
+
blueprint: {
|
|
6
|
+
screen_id: blueprint.screen.id,
|
|
7
|
+
version: blueprint.version,
|
|
8
|
+
},
|
|
9
|
+
implementation: {
|
|
10
|
+
framework: '',
|
|
11
|
+
route: '',
|
|
12
|
+
files: [],
|
|
13
|
+
},
|
|
14
|
+
node_mappings: blueprint.nodes.map((node) => ({
|
|
15
|
+
node_id: node.id,
|
|
16
|
+
status: 'unmapped',
|
|
17
|
+
component: node.type,
|
|
18
|
+
file: '',
|
|
19
|
+
selector: `[data-aub-node="${node.id}"]`,
|
|
20
|
+
notes: '',
|
|
21
|
+
})),
|
|
22
|
+
acceptance_results: blueprint.acceptance.map((item) => ({
|
|
23
|
+
acceptance_id: item.id,
|
|
24
|
+
status: 'needs-review',
|
|
25
|
+
evidence: [],
|
|
26
|
+
notes: '',
|
|
27
|
+
})),
|
|
28
|
+
unresolved: [],
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function verifyImplementationReport(blueprint, report) {
|
|
33
|
+
const errors = [];
|
|
34
|
+
if (report.blueprint?.screen_id !== blueprint.screen.id) {
|
|
35
|
+
errors.push(`Report screen_id "${report.blueprint?.screen_id ?? ''}" does not match "${blueprint.screen.id}".`);
|
|
36
|
+
}
|
|
37
|
+
if (report.blueprint?.version !== blueprint.version) {
|
|
38
|
+
errors.push(`Report Blueprint version "${report.blueprint?.version ?? ''}" does not match "${blueprint.version}".`);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const nodeIds = new Set(blueprint.nodes.map((node) => node.id));
|
|
42
|
+
const mappingById = uniqueById(report.node_mappings ?? [], 'node_id', errors);
|
|
43
|
+
for (const nodeId of nodeIds) {
|
|
44
|
+
const mapping = mappingById.get(nodeId);
|
|
45
|
+
if (!mapping) errors.push(`Missing node mapping: ${nodeId}`);
|
|
46
|
+
else if (mapping.status !== 'mapped' || !mapping.file?.trim()) {
|
|
47
|
+
errors.push(`Node is not mapped to a file: ${nodeId}`);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
for (const nodeId of mappingById.keys()) {
|
|
51
|
+
if (!nodeIds.has(nodeId)) errors.push(`Unknown node mapping: ${nodeId}`);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const acceptanceById = new Map(blueprint.acceptance.map((item) => [item.id, item]));
|
|
55
|
+
const resultById = uniqueById(report.acceptance_results ?? [], 'acceptance_id', errors);
|
|
56
|
+
for (const [acceptanceId, acceptance] of acceptanceById) {
|
|
57
|
+
const result = resultById.get(acceptanceId);
|
|
58
|
+
if (!result) {
|
|
59
|
+
errors.push(`Missing acceptance result: ${acceptanceId}`);
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
if (result.status !== 'pass') {
|
|
63
|
+
errors.push(`${acceptance.priority} acceptance is not passing: ${acceptanceId} (${result.status})`);
|
|
64
|
+
}
|
|
65
|
+
if (!Array.isArray(result.evidence) || result.evidence.length === 0) {
|
|
66
|
+
errors.push(`Acceptance result has no evidence: ${acceptanceId}`);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
for (const acceptanceId of resultById.keys()) {
|
|
70
|
+
if (!acceptanceById.has(acceptanceId)) errors.push(`Unknown acceptance result: ${acceptanceId}`);
|
|
71
|
+
}
|
|
72
|
+
if ((report.unresolved ?? []).length > 0) {
|
|
73
|
+
errors.push(`Report has ${report.unresolved.length} unresolved item(s).`);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return {
|
|
77
|
+
ready: errors.length === 0,
|
|
78
|
+
errors,
|
|
79
|
+
summary: {
|
|
80
|
+
nodes_total: blueprint.nodes.length,
|
|
81
|
+
nodes_mapped: [...mappingById.values()].filter((item) => item.status === 'mapped' && item.file?.trim()).length,
|
|
82
|
+
acceptance_total: blueprint.acceptance.length,
|
|
83
|
+
acceptance_passed: [...resultById.values()].filter((item) => item.status === 'pass').length,
|
|
84
|
+
unresolved: report.unresolved?.length ?? 0,
|
|
85
|
+
},
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function uniqueById(items, field, errors) {
|
|
90
|
+
const map = new Map();
|
|
91
|
+
for (const item of items) {
|
|
92
|
+
const id = item?.[field];
|
|
93
|
+
if (map.has(id)) errors.push(`Duplicate ${field}: ${id}`);
|
|
94
|
+
else map.set(id, item);
|
|
95
|
+
}
|
|
96
|
+
return map;
|
|
97
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { readFile, readdir, stat, writeFile } from 'node:fs/promises';
|
|
4
|
+
import { extname, relative, resolve } from 'node:path';
|
|
5
|
+
import { importAngularComponent } from './angular-importer.lib.mjs';
|
|
6
|
+
|
|
7
|
+
const args = process.argv.slice(2);
|
|
8
|
+
const options = parseArgs(args);
|
|
9
|
+
if (options.sources.length === 0 || !options.output) {
|
|
10
|
+
console.error('Usage: pnpm import:angular -- <file-or-directory...> --output screen.ui.json [--entry app-selector]');
|
|
11
|
+
process.exit(2);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const cwd = process.cwd();
|
|
15
|
+
const sourcePaths = await expandSources(options.sources);
|
|
16
|
+
const files = await Promise.all(sourcePaths.map(async (path) => ({
|
|
17
|
+
path: relative(commonBase(sourcePaths), path).replace(/\\/g, '/'),
|
|
18
|
+
content: await readFile(path, 'utf8'),
|
|
19
|
+
})));
|
|
20
|
+
const result = await importAngularComponent(files, { entry: options.entry });
|
|
21
|
+
await writeFile(resolve(options.output), `${JSON.stringify(result.blueprint, null, 2)}\n`, 'utf8');
|
|
22
|
+
|
|
23
|
+
const warnings = result.diagnostics.filter((item) => item.severity === 'warning').length;
|
|
24
|
+
console.error(`✓ imported ${files.length} files into ${options.output}`);
|
|
25
|
+
console.error(` ${result.blueprint.nodes.length} nodes, ${result.blueprint.interactions.length} interactions, ${warnings} warnings`);
|
|
26
|
+
|
|
27
|
+
function parseArgs(values) {
|
|
28
|
+
const result = { sources: [], entry: undefined, output: undefined };
|
|
29
|
+
for (let index = 0; index < values.length; index += 1) {
|
|
30
|
+
if (values[index] === '--entry') result.entry = values[++index];
|
|
31
|
+
else if (values[index] === '--output' || values[index] === '-o') result.output = values[++index];
|
|
32
|
+
else result.sources.push(values[index]);
|
|
33
|
+
}
|
|
34
|
+
return result;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
async function expandSources(sources) {
|
|
38
|
+
const result = [];
|
|
39
|
+
for (const source of sources) {
|
|
40
|
+
const path = resolve(cwd, source);
|
|
41
|
+
const info = await stat(path);
|
|
42
|
+
if (info.isDirectory()) result.push(...await walk(path));
|
|
43
|
+
else if (isSource(path)) result.push(path);
|
|
44
|
+
}
|
|
45
|
+
return [...new Set(result)].sort();
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async function walk(directory) {
|
|
49
|
+
const result = [];
|
|
50
|
+
for (const entry of await readdir(directory, { withFileTypes: true })) {
|
|
51
|
+
const path = resolve(directory, entry.name);
|
|
52
|
+
if (entry.isDirectory()) result.push(...await walk(path));
|
|
53
|
+
else if (isSource(path)) result.push(path);
|
|
54
|
+
}
|
|
55
|
+
return result;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function isSource(path) {
|
|
59
|
+
return ['.html', '.scss', '.css', '.ts'].includes(extname(path).toLowerCase())
|
|
60
|
+
&& !/\.spec\.ts$/i.test(path);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function commonBase(paths) {
|
|
64
|
+
if (paths.length === 1) return resolve(paths[0], '..');
|
|
65
|
+
const parts = paths.map((path) => resolve(path).split('/'));
|
|
66
|
+
const common = [];
|
|
67
|
+
for (let index = 0; index < Math.min(...parts.map((value) => value.length)); index += 1) {
|
|
68
|
+
if (parts.every((value) => value[index] === parts[0][index])) common.push(parts[0][index]);
|
|
69
|
+
else break;
|
|
70
|
+
}
|
|
71
|
+
return common.join('/') || '/';
|
|
72
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { readFile, writeFile } from 'node:fs/promises';
|
|
3
|
+
import { dirname, resolve } from 'node:path';
|
|
4
|
+
import Ajv2020 from 'ajv/dist/2020.js';
|
|
5
|
+
import addFormats from 'ajv-formats';
|
|
6
|
+
import { importDesignBridge } from './design-bridge.lib.mjs';
|
|
7
|
+
import { buildKnownTypes } from './registry.lib.mjs';
|
|
8
|
+
import { validateBlueprintSemantics } from './validate-blueprint.lib.mjs';
|
|
9
|
+
|
|
10
|
+
const args = process.argv.slice(2);
|
|
11
|
+
const inputPath = args.find((arg) => !arg.startsWith('-'));
|
|
12
|
+
const outputIndex = args.findIndex((arg) => arg === '--output' || arg === '-o');
|
|
13
|
+
const registryIndex = args.findIndex((arg) => arg === '--registry');
|
|
14
|
+
const outputPath = outputIndex >= 0 ? args[outputIndex + 1] : undefined;
|
|
15
|
+
const registryPath = registryIndex >= 0 ? args[registryIndex + 1] : undefined;
|
|
16
|
+
|
|
17
|
+
if (!inputPath || !outputPath) {
|
|
18
|
+
console.error('Usage: pnpm import:design -- <file.aub.bridge.json> --output <screen.ui.json> [--registry aub.registry.json]');
|
|
19
|
+
process.exit(2);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const root = resolve(import.meta.dirname, '..');
|
|
23
|
+
const [bridgeSchema, blueprintSchema, input] = await Promise.all([
|
|
24
|
+
readFile(resolve(root, 'schema/design-bridge.schema.json'), 'utf8').then(JSON.parse),
|
|
25
|
+
readFile(resolve(root, 'schema/ui-blueprint.schema.json'), 'utf8').then(JSON.parse),
|
|
26
|
+
readFile(resolve(inputPath), 'utf8').then(JSON.parse),
|
|
27
|
+
]);
|
|
28
|
+
const ajv = new Ajv2020({ allErrors: true, strict: true });
|
|
29
|
+
addFormats(ajv);
|
|
30
|
+
ajv.addSchema(blueprintSchema);
|
|
31
|
+
const validateBridge = ajv.compile(bridgeSchema);
|
|
32
|
+
if (!validateBridge(input)) {
|
|
33
|
+
for (const error of validateBridge.errors ?? []) {
|
|
34
|
+
console.error(` ${error.instancePath || '(root)'} ${error.message}`);
|
|
35
|
+
}
|
|
36
|
+
process.exit(1);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const result = importDesignBridge(input);
|
|
40
|
+
const validateBlueprint = ajv.getSchema(blueprintSchema.$id);
|
|
41
|
+
const schemaOk = validateBlueprint(result.blueprint);
|
|
42
|
+
const knownTypes = await buildKnownTypes({
|
|
43
|
+
extensionPath: registryPath ? resolve(registryPath) : null,
|
|
44
|
+
startDir: dirname(resolve(inputPath)),
|
|
45
|
+
});
|
|
46
|
+
const semanticErrors = schemaOk
|
|
47
|
+
? validateBlueprintSemantics(result.blueprint, { knownTypes: knownTypes.knownTypes })
|
|
48
|
+
: [];
|
|
49
|
+
if (!schemaOk || semanticErrors.length > 0) {
|
|
50
|
+
for (const error of validateBlueprint.errors ?? []) {
|
|
51
|
+
console.error(` ${error.instancePath || '(root)'} ${error.message}`);
|
|
52
|
+
}
|
|
53
|
+
for (const error of semanticErrors) console.error(` semantic: ${error}`);
|
|
54
|
+
process.exit(1);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
await writeFile(resolve(outputPath), `${JSON.stringify(result.blueprint, null, 2)}\n`, 'utf8');
|
|
58
|
+
console.error(`✓ imported ${input.source.kind} frame ${input.source.frame_id} into ${outputPath}`);
|
|
59
|
+
console.error(` ${result.blueprint.nodes.length} semantic nodes with complete source mapping`);
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export interface BlueprintLock {
|
|
2
|
+
$schema: string;
|
|
3
|
+
version: string;
|
|
4
|
+
source_file: string;
|
|
5
|
+
exported_at: string;
|
|
6
|
+
source_editor_version: string;
|
|
7
|
+
hashes: Record<string, string>;
|
|
8
|
+
counts: {
|
|
9
|
+
nodes: number;
|
|
10
|
+
interactions: number;
|
|
11
|
+
responsive: number;
|
|
12
|
+
acceptance: number;
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function createBlueprintLock(
|
|
17
|
+
blueprint: Record<string, any>,
|
|
18
|
+
options?: {
|
|
19
|
+
sourceFile?: string;
|
|
20
|
+
exportedAt?: string;
|
|
21
|
+
sourceEditorVersion?: string;
|
|
22
|
+
}
|
|
23
|
+
): BlueprintLock;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
|
|
3
|
+
export function createBlueprintLock(
|
|
4
|
+
blueprint,
|
|
5
|
+
{
|
|
6
|
+
sourceFile = '',
|
|
7
|
+
exportedAt = new Date().toISOString(),
|
|
8
|
+
sourceEditorVersion = '0.3.0',
|
|
9
|
+
} = {}
|
|
10
|
+
) {
|
|
11
|
+
const sortedIds = [...blueprint.nodes.map((node) => node.id)].sort();
|
|
12
|
+
const layoutSubset = blueprint.nodes.map((node) => ({
|
|
13
|
+
id: node.id,
|
|
14
|
+
type: node.type,
|
|
15
|
+
layout: node.layout ?? null,
|
|
16
|
+
placements: node.placements ?? null,
|
|
17
|
+
}));
|
|
18
|
+
const treeSubset = blueprint.nodes.map((node) => ({
|
|
19
|
+
id: node.id,
|
|
20
|
+
type: node.type,
|
|
21
|
+
parent_id: node.parent_id,
|
|
22
|
+
children: node.children ?? [],
|
|
23
|
+
}));
|
|
24
|
+
|
|
25
|
+
return {
|
|
26
|
+
$schema: 'https://henrylau1103.github.io/AUB/schema/ui-blueprint-lock.schema.json',
|
|
27
|
+
version: blueprint.version,
|
|
28
|
+
source_file: sourceFile,
|
|
29
|
+
exported_at: exportedAt,
|
|
30
|
+
source_editor_version: sourceEditorVersion,
|
|
31
|
+
hashes: {
|
|
32
|
+
blueprint: sha256(stableStringify(blueprint)),
|
|
33
|
+
node_ids: sha256(stableStringify(sortedIds)),
|
|
34
|
+
layout: sha256(stableStringify(layoutSubset)),
|
|
35
|
+
design_system: sha256(stableStringify(blueprint.design_system ?? null)),
|
|
36
|
+
component_tree: sha256(stableStringify(treeSubset)),
|
|
37
|
+
interactions: sha256(stableStringify(blueprint.interactions ?? [])),
|
|
38
|
+
acceptance: sha256(stableStringify(blueprint.acceptance ?? [])),
|
|
39
|
+
},
|
|
40
|
+
counts: {
|
|
41
|
+
nodes: blueprint.nodes.length,
|
|
42
|
+
interactions: (blueprint.interactions ?? []).length,
|
|
43
|
+
responsive: (blueprint.responsive ?? []).length,
|
|
44
|
+
acceptance: (blueprint.acceptance ?? []).length,
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function sha256(value) {
|
|
50
|
+
return `sha256:${createHash('sha256').update(value).digest('hex')}`;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function stableStringify(value) {
|
|
54
|
+
if (value === null || typeof value !== 'object') return JSON.stringify(value);
|
|
55
|
+
if (Array.isArray(value)) return `[${value.map(stableStringify).join(',')}]`;
|
|
56
|
+
const keys = Object.keys(value).sort();
|
|
57
|
+
return `{${keys.map((key) => `${JSON.stringify(key)}:${stableStringify(value[key])}`).join(',')}}`;
|
|
58
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Lock a UI Blueprint into a frozen acceptance snapshot (plan §6.4).
|
|
3
|
+
// Usage: node scripts/lock-blueprint.mjs <input.ui.json> <output.ui.lock.json>
|
|
4
|
+
//
|
|
5
|
+
// The lock file contains SHA-256 hashes of structural subsets plus provenance.
|
|
6
|
+
// If any of these change after the lock is created, the design has drifted
|
|
7
|
+
// and agents should NOT silently apply the change.
|
|
8
|
+
|
|
9
|
+
import { readFile, writeFile } from 'node:fs/promises';
|
|
10
|
+
import { resolve } from 'node:path';
|
|
11
|
+
import { createBlueprintLock } from './lock-blueprint.lib.mjs';
|
|
12
|
+
|
|
13
|
+
async function main() {
|
|
14
|
+
const args = process.argv.slice(2);
|
|
15
|
+
if (args.length !== 2) {
|
|
16
|
+
console.error('Usage: node scripts/lock-blueprint.mjs <input.ui.json> <output.ui.lock.json>');
|
|
17
|
+
process.exit(2);
|
|
18
|
+
}
|
|
19
|
+
const [inputPath, outputPath] = args;
|
|
20
|
+
const blueprint = JSON.parse(await readFile(resolve(inputPath), 'utf8'));
|
|
21
|
+
|
|
22
|
+
const lock = createBlueprintLock(blueprint, {
|
|
23
|
+
sourceFile: inputPath,
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
await writeFile(resolve(outputPath), JSON.stringify(lock, null, 2) + '\n', 'utf8');
|
|
27
|
+
console.error(`✓ wrote ${outputPath}`);
|
|
28
|
+
for (const [k, v] of Object.entries(lock.hashes)) {
|
|
29
|
+
console.error(` ${k}: ${v.slice(0, 16)}...`);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
main().catch((err) => {
|
|
34
|
+
console.error(err);
|
|
35
|
+
process.exit(1);
|
|
36
|
+
});
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Upgrade a Blueprint to the current schema version.
|
|
3
|
+
// Usage: node scripts/migrate-blueprint-cli.mjs <input.ui.json|yaml> <output.ui.json|yaml>
|
|
4
|
+
|
|
5
|
+
import { readFile, writeFile } from 'node:fs/promises';
|
|
6
|
+
import { extname, resolve } from 'node:path';
|
|
7
|
+
import yaml from 'js-yaml';
|
|
8
|
+
import { migrateBlueprint } from './migrate-blueprint.mjs';
|
|
9
|
+
|
|
10
|
+
const [inputPath, outputPath] = process.argv.slice(2);
|
|
11
|
+
if (!inputPath || !outputPath) {
|
|
12
|
+
console.error('Usage: node scripts/migrate-blueprint-cli.mjs <input.ui.json|yaml> <output.ui.json|yaml>');
|
|
13
|
+
process.exit(2);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const inputExtension = extname(inputPath).toLowerCase();
|
|
17
|
+
const outputExtension = extname(outputPath).toLowerCase();
|
|
18
|
+
const raw = await readFile(resolve(inputPath), 'utf8');
|
|
19
|
+
const source = inputExtension === '.yaml' || inputExtension === '.yml'
|
|
20
|
+
? yaml.load(raw)
|
|
21
|
+
: JSON.parse(raw);
|
|
22
|
+
const migrated = migrateBlueprint(source);
|
|
23
|
+
const output = outputExtension === '.yaml' || outputExtension === '.yml'
|
|
24
|
+
? yaml.dump(migrated, { noRefs: true, lineWidth: 120, sortKeys: false })
|
|
25
|
+
: `${JSON.stringify(migrated, null, 2)}\n`;
|
|
26
|
+
|
|
27
|
+
await writeFile(resolve(outputPath), output, 'utf8');
|
|
28
|
+
console.error(`✓ migrated ${inputPath} → ${outputPath}`);
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
const CURRENT_VERSION = '0.3.0';
|
|
2
|
+
|
|
3
|
+
export function migrateBlueprint(input) {
|
|
4
|
+
if (!input || typeof input !== 'object' || Array.isArray(input)) {
|
|
5
|
+
throw new TypeError('Blueprint must be an object.');
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const sourceVersion = typeof input.version === 'string' ? input.version : '0.1.0';
|
|
9
|
+
if (!['0.1.0', '0.2.0', CURRENT_VERSION].includes(sourceVersion)) {
|
|
10
|
+
throw new Error(`Unsupported Blueprint version: ${sourceVersion}`);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const blueprint = structuredClone(input);
|
|
14
|
+
blueprint.version = CURRENT_VERSION;
|
|
15
|
+
blueprint.design_system ??= defaultDesignSystem();
|
|
16
|
+
blueprint.nodes = Array.isArray(blueprint.nodes)
|
|
17
|
+
? blueprint.nodes.map((node) => migrateNode(node))
|
|
18
|
+
: [];
|
|
19
|
+
|
|
20
|
+
return blueprint;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function defaultDesignSystem() {
|
|
24
|
+
return {
|
|
25
|
+
name: 'AUB Neutral',
|
|
26
|
+
colors: {
|
|
27
|
+
'surface.canvas': '#f8fafc',
|
|
28
|
+
'surface.panel': '#ffffff',
|
|
29
|
+
'surface.subtle': '#f1f5f9',
|
|
30
|
+
'text.primary': '#0f172a',
|
|
31
|
+
'text.secondary': '#64748b',
|
|
32
|
+
'border.default': '#cbd5e1',
|
|
33
|
+
'action.primary': '#2563eb',
|
|
34
|
+
'action.primary.text': '#ffffff',
|
|
35
|
+
'status.success': '#047857',
|
|
36
|
+
'status.warning': '#b45309',
|
|
37
|
+
'status.danger': '#b91c1c'
|
|
38
|
+
},
|
|
39
|
+
typography: {
|
|
40
|
+
'heading.page': '700 28px/1.2 system-ui',
|
|
41
|
+
'heading.section': '650 18px/1.3 system-ui',
|
|
42
|
+
'body.default': '400 14px/1.5 system-ui',
|
|
43
|
+
'body.small': '400 12px/1.4 system-ui',
|
|
44
|
+
'label.default': '600 12px/1.3 system-ui'
|
|
45
|
+
},
|
|
46
|
+
spacing: {
|
|
47
|
+
'space.1': '4px',
|
|
48
|
+
'space.2': '8px',
|
|
49
|
+
'space.3': '12px',
|
|
50
|
+
'space.4': '16px',
|
|
51
|
+
'space.5': '24px',
|
|
52
|
+
'space.6': '32px'
|
|
53
|
+
},
|
|
54
|
+
radii: {
|
|
55
|
+
'radius.control': '6px',
|
|
56
|
+
'radius.panel': '8px',
|
|
57
|
+
'radius.round': '999px'
|
|
58
|
+
},
|
|
59
|
+
shadows: {
|
|
60
|
+
'shadow.panel': '0 8px 24px rgba(15, 23, 42, 0.12)',
|
|
61
|
+
'shadow.overlay': '0 18px 48px rgba(15, 23, 42, 0.22)'
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function migrateNode(input) {
|
|
67
|
+
const node = { ...input };
|
|
68
|
+
if (node.layout) {
|
|
69
|
+
node.layout = {
|
|
70
|
+
mode: node.layout.mode ?? 'auto',
|
|
71
|
+
...node.layout
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
if (node.placements && typeof node.placements === 'object') {
|
|
75
|
+
node.placements = Object.fromEntries(
|
|
76
|
+
Object.entries(node.placements).map(([viewport, placement]) => [
|
|
77
|
+
viewport,
|
|
78
|
+
normalizePlacement(placement)
|
|
79
|
+
])
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
return node;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function normalizePlacement(input) {
|
|
86
|
+
const placement = { ...input };
|
|
87
|
+
for (const key of ['x', 'y', 'width', 'height']) {
|
|
88
|
+
if (typeof placement[key] !== 'number' || !Number.isFinite(placement[key])) {
|
|
89
|
+
throw new Error(`Invalid placement.${key}`);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return placement;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export { CURRENT_VERSION };
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { cp, mkdir, rm, stat } from 'node:fs/promises';
|
|
3
|
+
import { dirname, join, resolve } from 'node:path';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
5
|
+
|
|
6
|
+
const repoRoot = resolve(dirname(fileURLToPath(import.meta.url)), '..');
|
|
7
|
+
const packageRoot = join(repoRoot, 'packages', 'workspace-cli');
|
|
8
|
+
const vendorRoot = join(packageRoot, 'vendor', 'aub');
|
|
9
|
+
|
|
10
|
+
async function assertDir(path, label) {
|
|
11
|
+
try {
|
|
12
|
+
const info = await stat(path);
|
|
13
|
+
if (!info.isDirectory()) throw new Error(`${label} is not a directory: ${path}`);
|
|
14
|
+
} catch {
|
|
15
|
+
throw new Error(`${label} is missing. Build first with: pnpm workspace:package`);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
await assertDir(join(repoRoot, 'apps', 'mcp-server', 'dist'), 'MCP server dist');
|
|
20
|
+
await assertDir(join(repoRoot, 'apps', 'editor', 'dist'), 'Editor dist');
|
|
21
|
+
|
|
22
|
+
await rm(vendorRoot, { recursive: true, force: true });
|
|
23
|
+
await mkdir(vendorRoot, { recursive: true });
|
|
24
|
+
|
|
25
|
+
await cp(join(repoRoot, 'schema'), join(vendorRoot, 'schema'), { recursive: true });
|
|
26
|
+
await cp(join(repoRoot, 'scripts'), join(vendorRoot, 'scripts'), { recursive: true });
|
|
27
|
+
await mkdir(join(vendorRoot, 'docs'), { recursive: true });
|
|
28
|
+
await cp(join(repoRoot, 'docs', 'agent-handoff.md'), join(vendorRoot, 'docs', 'agent-handoff.md'));
|
|
29
|
+
await cp(join(repoRoot, 'docs', 'agent-handoff.zh-Hant.md'), join(vendorRoot, 'docs', 'agent-handoff.zh-Hant.md'));
|
|
30
|
+
await cp(join(repoRoot, 'docs', 'template-authoring-agent.md'), join(vendorRoot, 'docs', 'template-authoring-agent.md'));
|
|
31
|
+
await cp(join(repoRoot, 'apps', 'mcp-server', 'dist'), join(vendorRoot, 'apps', 'mcp-server', 'dist'), { recursive: true });
|
|
32
|
+
await cp(join(repoRoot, 'apps', 'editor', 'dist'), join(vendorRoot, 'apps', 'editor', 'dist'), { recursive: true });
|
|
33
|
+
|
|
34
|
+
console.error(`Prepared aub-workspace vendor payload at ${vendorRoot}`);
|