@kbediako/codex-orchestrator 0.1.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 +7 -0
- package/README.md +238 -0
- package/dist/bin/codex-orchestrator.js +507 -0
- package/dist/orchestrator/src/agents/builder.js +16 -0
- package/dist/orchestrator/src/agents/index.js +4 -0
- package/dist/orchestrator/src/agents/planner.js +17 -0
- package/dist/orchestrator/src/agents/reviewer.js +13 -0
- package/dist/orchestrator/src/agents/tester.js +13 -0
- package/dist/orchestrator/src/cli/adapters/CommandBuilder.js +20 -0
- package/dist/orchestrator/src/cli/adapters/CommandPlanner.js +164 -0
- package/dist/orchestrator/src/cli/adapters/CommandReviewer.js +32 -0
- package/dist/orchestrator/src/cli/adapters/CommandTester.js +33 -0
- package/dist/orchestrator/src/cli/adapters/index.js +4 -0
- package/dist/orchestrator/src/cli/config/userConfig.js +28 -0
- package/dist/orchestrator/src/cli/doctor.js +48 -0
- package/dist/orchestrator/src/cli/events/runEvents.js +84 -0
- package/dist/orchestrator/src/cli/exec/command.js +56 -0
- package/dist/orchestrator/src/cli/exec/context.js +108 -0
- package/dist/orchestrator/src/cli/exec/experience.js +77 -0
- package/dist/orchestrator/src/cli/exec/finalization.js +140 -0
- package/dist/orchestrator/src/cli/exec/learning.js +62 -0
- package/dist/orchestrator/src/cli/exec/stageRunner.js +71 -0
- package/dist/orchestrator/src/cli/exec/summary.js +109 -0
- package/dist/orchestrator/src/cli/exec/telemetry.js +18 -0
- package/dist/orchestrator/src/cli/exec/tfgrpo.js +200 -0
- package/dist/orchestrator/src/cli/exec/tfgrpoArtifacts.js +19 -0
- package/dist/orchestrator/src/cli/exec/types.js +1 -0
- package/dist/orchestrator/src/cli/init.js +64 -0
- package/dist/orchestrator/src/cli/mcp.js +124 -0
- package/dist/orchestrator/src/cli/metrics/metricsAggregator.js +404 -0
- package/dist/orchestrator/src/cli/metrics/metricsRecorder.js +138 -0
- package/dist/orchestrator/src/cli/orchestrator.js +554 -0
- package/dist/orchestrator/src/cli/pipelines/defaultDiagnostics.js +32 -0
- package/dist/orchestrator/src/cli/pipelines/designReference.js +72 -0
- package/dist/orchestrator/src/cli/pipelines/hiFiDesignToolkit.js +71 -0
- package/dist/orchestrator/src/cli/pipelines/index.js +34 -0
- package/dist/orchestrator/src/cli/run/environment.js +24 -0
- package/dist/orchestrator/src/cli/run/manifest.js +367 -0
- package/dist/orchestrator/src/cli/run/manifestPersister.js +88 -0
- package/dist/orchestrator/src/cli/run/runPaths.js +30 -0
- package/dist/orchestrator/src/cli/selfCheck.js +12 -0
- package/dist/orchestrator/src/cli/services/commandRunner.js +420 -0
- package/dist/orchestrator/src/cli/services/controlPlaneService.js +107 -0
- package/dist/orchestrator/src/cli/services/execRuntime.js +69 -0
- package/dist/orchestrator/src/cli/services/pipelineResolver.js +47 -0
- package/dist/orchestrator/src/cli/services/runPreparation.js +82 -0
- package/dist/orchestrator/src/cli/services/runSummaryWriter.js +35 -0
- package/dist/orchestrator/src/cli/services/schedulerService.js +42 -0
- package/dist/orchestrator/src/cli/tasks/taskMetadata.js +19 -0
- package/dist/orchestrator/src/cli/telemetry/schema.js +8 -0
- package/dist/orchestrator/src/cli/types.js +1 -0
- package/dist/orchestrator/src/cli/ui/HudApp.js +112 -0
- package/dist/orchestrator/src/cli/ui/controller.js +26 -0
- package/dist/orchestrator/src/cli/ui/store.js +240 -0
- package/dist/orchestrator/src/cli/utils/enforcementMode.js +12 -0
- package/dist/orchestrator/src/cli/utils/fs.js +8 -0
- package/dist/orchestrator/src/cli/utils/interactive.js +25 -0
- package/dist/orchestrator/src/cli/utils/jsonlWriter.js +10 -0
- package/dist/orchestrator/src/cli/utils/optionalDeps.js +30 -0
- package/dist/orchestrator/src/cli/utils/packageInfo.js +25 -0
- package/dist/orchestrator/src/cli/utils/planFormatter.js +49 -0
- package/dist/orchestrator/src/cli/utils/runId.js +7 -0
- package/dist/orchestrator/src/cli/utils/specGuardRunner.js +26 -0
- package/dist/orchestrator/src/cli/utils/strings.js +8 -0
- package/dist/orchestrator/src/cli/utils/time.js +6 -0
- package/dist/orchestrator/src/control-plane/drift-reporter.js +109 -0
- package/dist/orchestrator/src/control-plane/index.js +3 -0
- package/dist/orchestrator/src/control-plane/request-builder.js +217 -0
- package/dist/orchestrator/src/control-plane/types.js +1 -0
- package/dist/orchestrator/src/control-plane/validator.js +50 -0
- package/dist/orchestrator/src/credentials/CredentialBroker.js +1 -0
- package/dist/orchestrator/src/events/EventBus.js +25 -0
- package/dist/orchestrator/src/learning/crystalizer.js +108 -0
- package/dist/orchestrator/src/learning/harvester.js +146 -0
- package/dist/orchestrator/src/learning/manifest.js +56 -0
- package/dist/orchestrator/src/learning/runner.js +177 -0
- package/dist/orchestrator/src/learning/validator.js +164 -0
- package/dist/orchestrator/src/logger.js +20 -0
- package/dist/orchestrator/src/manager.js +388 -0
- package/dist/orchestrator/src/persistence/ArtifactStager.js +95 -0
- package/dist/orchestrator/src/persistence/ExperienceStore.js +210 -0
- package/dist/orchestrator/src/persistence/PersistenceCoordinator.js +65 -0
- package/dist/orchestrator/src/persistence/RunManifestWriter.js +23 -0
- package/dist/orchestrator/src/persistence/TaskStateStore.js +172 -0
- package/dist/orchestrator/src/persistence/identifierGuards.js +1 -0
- package/dist/orchestrator/src/persistence/lockFile.js +26 -0
- package/dist/orchestrator/src/persistence/sanitizeIdentifier.js +26 -0
- package/dist/orchestrator/src/persistence/sanitizeRunId.js +8 -0
- package/dist/orchestrator/src/persistence/sanitizeTaskId.js +8 -0
- package/dist/orchestrator/src/persistence/writeAtomicFile.js +4 -0
- package/dist/orchestrator/src/privacy/guard.js +111 -0
- package/dist/orchestrator/src/scheduler/index.js +1 -0
- package/dist/orchestrator/src/scheduler/plan.js +171 -0
- package/dist/orchestrator/src/scheduler/types.js +1 -0
- package/dist/orchestrator/src/sync/CloudRunsClient.js +1 -0
- package/dist/orchestrator/src/sync/CloudRunsHttpClient.js +82 -0
- package/dist/orchestrator/src/sync/CloudSyncWorker.js +206 -0
- package/dist/orchestrator/src/sync/createCloudSyncWorker.js +15 -0
- package/dist/orchestrator/src/types.js +1 -0
- package/dist/orchestrator/src/utils/atomicWrite.js +15 -0
- package/dist/orchestrator/src/utils/errorMessage.js +14 -0
- package/dist/orchestrator/src/utils/executionMode.js +69 -0
- package/dist/packages/control-plane-schemas/src/index.js +1 -0
- package/dist/packages/control-plane-schemas/src/run-request.js +548 -0
- package/dist/packages/orchestrator/src/exec/handle-service.js +203 -0
- package/dist/packages/orchestrator/src/exec/session-manager.js +147 -0
- package/dist/packages/orchestrator/src/exec/unified-exec.js +432 -0
- package/dist/packages/orchestrator/src/index.js +3 -0
- package/dist/packages/orchestrator/src/instructions/loader.js +101 -0
- package/dist/packages/orchestrator/src/instructions/promptPacks.js +151 -0
- package/dist/packages/orchestrator/src/notifications/index.js +74 -0
- package/dist/packages/orchestrator/src/telemetry/otel-exporter.js +142 -0
- package/dist/packages/orchestrator/src/tool-orchestrator.js +161 -0
- package/dist/packages/sdk-node/src/orchestrator.js +195 -0
- package/dist/packages/shared/config/designConfig.js +495 -0
- package/dist/packages/shared/config/env.js +37 -0
- package/dist/packages/shared/config/index.js +2 -0
- package/dist/packages/shared/design-artifacts/writer.js +221 -0
- package/dist/packages/shared/events/serializer.js +84 -0
- package/dist/packages/shared/events/types.js +1 -0
- package/dist/packages/shared/manifest/artifactUtils.js +36 -0
- package/dist/packages/shared/manifest/designArtifacts.js +665 -0
- package/dist/packages/shared/manifest/fileIO.js +29 -0
- package/dist/packages/shared/manifest/toolRuns.js +78 -0
- package/dist/packages/shared/manifest/toolkitArtifacts.js +223 -0
- package/dist/packages/shared/manifest/types.js +5 -0
- package/dist/packages/shared/manifest/validator.js +73 -0
- package/dist/packages/shared/manifest/writer.js +2 -0
- package/dist/packages/shared/streams/stdio.js +112 -0
- package/dist/scripts/design/pipeline/advanced-assets.js +466 -0
- package/dist/scripts/design/pipeline/componentize.js +74 -0
- package/dist/scripts/design/pipeline/context.js +34 -0
- package/dist/scripts/design/pipeline/extract.js +249 -0
- package/dist/scripts/design/pipeline/optionalDeps.js +107 -0
- package/dist/scripts/design/pipeline/prepare.js +46 -0
- package/dist/scripts/design/pipeline/reference.js +94 -0
- package/dist/scripts/design/pipeline/state.js +206 -0
- package/dist/scripts/design/pipeline/toolkit/common.js +94 -0
- package/dist/scripts/design/pipeline/toolkit/extract.js +258 -0
- package/dist/scripts/design/pipeline/toolkit/publish.js +202 -0
- package/dist/scripts/design/pipeline/toolkit/publishActions.js +12 -0
- package/dist/scripts/design/pipeline/toolkit/reference.js +846 -0
- package/dist/scripts/design/pipeline/toolkit/snapshot.js +882 -0
- package/dist/scripts/design/pipeline/toolkit/tokens.js +456 -0
- package/dist/scripts/design/pipeline/visual-regression.js +137 -0
- package/dist/scripts/design/pipeline/write-artifacts.js +61 -0
- package/package.json +97 -0
- package/schemas/manifest.json +1064 -0
- package/templates/README.md +12 -0
- package/templates/codex/mcp-client.json +8 -0
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { readFile } from 'node:fs/promises';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
export async function loadToolkitPermit(repoRoot) {
|
|
4
|
+
const permitPath = join(repoRoot, 'compliance', 'permit.json');
|
|
5
|
+
try {
|
|
6
|
+
const raw = await readFile(permitPath, 'utf8');
|
|
7
|
+
return JSON.parse(raw);
|
|
8
|
+
}
|
|
9
|
+
catch (error) {
|
|
10
|
+
const nodeError = error;
|
|
11
|
+
if (nodeError?.code === 'ENOENT') {
|
|
12
|
+
console.warn('[design-toolkit] compliance/permit.json not found; proceeding without permit enforcement');
|
|
13
|
+
return { allowedSources: [] };
|
|
14
|
+
}
|
|
15
|
+
throw error;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
export function ensureSourcePermitted(url, permit) {
|
|
19
|
+
const allowed = new Set((permit.allowedSources ?? [])
|
|
20
|
+
.map((entry) => {
|
|
21
|
+
try {
|
|
22
|
+
return new URL(entry.origin).origin;
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
})
|
|
28
|
+
.filter((origin) => Boolean(origin)));
|
|
29
|
+
const origin = new URL(url).origin;
|
|
30
|
+
if (allowed.size === 0 || allowed.has(origin)) {
|
|
31
|
+
return true;
|
|
32
|
+
}
|
|
33
|
+
console.warn(`[design-toolkit] Extraction target ${origin} is not listed in compliance/permit.json; continuing without permit match`);
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
export function resolveToolkitSources(pipeline, metadata) {
|
|
37
|
+
const sourceList = pipeline.sources.length > 0 ? pipeline.sources : buildSourcesFromCaptureUrls(metadata.captureUrls);
|
|
38
|
+
const breakpoints = pipeline.breakpoints.length > 0 ? pipeline.breakpoints : metadata.breakpoints;
|
|
39
|
+
const maskSelectors = pipeline.maskSelectors.length > 0 ? pipeline.maskSelectors : metadata.maskSelectors;
|
|
40
|
+
const finalBreakpoints = breakpoints.length > 0 ? breakpoints : defaultBreakpoints();
|
|
41
|
+
return sourceList.map((source, index) => ({
|
|
42
|
+
...source,
|
|
43
|
+
referenceUrl: source.referenceUrl ?? source.url,
|
|
44
|
+
slug: source.slug ?? slugifyToolkitValue(source.id ?? source.url, index),
|
|
45
|
+
breakpoints: finalBreakpoints,
|
|
46
|
+
maskSelectors
|
|
47
|
+
}));
|
|
48
|
+
}
|
|
49
|
+
export function computeToolkitRetention(pipeline, fallback) {
|
|
50
|
+
if (pipeline.retention) {
|
|
51
|
+
return {
|
|
52
|
+
days: pipeline.retention.days,
|
|
53
|
+
autoPurge: pipeline.retention.autoPurge,
|
|
54
|
+
policy: pipeline.retention.policy ?? fallback.policy
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
return fallback;
|
|
58
|
+
}
|
|
59
|
+
export function buildRetentionMetadata(retention, now) {
|
|
60
|
+
const expiryMs = now.getTime() + Math.max(1, retention.days) * 24 * 60 * 60 * 1000;
|
|
61
|
+
return {
|
|
62
|
+
...retention,
|
|
63
|
+
expiry: new Date(expiryMs).toISOString()
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
export function slugifyToolkitValue(value, index) {
|
|
67
|
+
const normalized = value
|
|
68
|
+
.toLowerCase()
|
|
69
|
+
.replace(/[^a-z0-9-_]+/g, '-')
|
|
70
|
+
.replace(/^-+|-+$/g, '')
|
|
71
|
+
.slice(0, 48);
|
|
72
|
+
if (normalized.length > 0) {
|
|
73
|
+
return normalized;
|
|
74
|
+
}
|
|
75
|
+
return `source-${index + 1}`;
|
|
76
|
+
}
|
|
77
|
+
function defaultBreakpoints() {
|
|
78
|
+
return [
|
|
79
|
+
{
|
|
80
|
+
id: 'desktop',
|
|
81
|
+
width: 1280,
|
|
82
|
+
height: 720
|
|
83
|
+
}
|
|
84
|
+
];
|
|
85
|
+
}
|
|
86
|
+
function buildSourcesFromCaptureUrls(urls) {
|
|
87
|
+
return urls.map((url, index) => ({
|
|
88
|
+
id: `source-${index + 1}`,
|
|
89
|
+
url,
|
|
90
|
+
referenceUrl: url,
|
|
91
|
+
slug: slugifyToolkitValue(url, index),
|
|
92
|
+
title: null
|
|
93
|
+
}));
|
|
94
|
+
}
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
import { copyFile, mkdir, writeFile } from 'node:fs/promises';
|
|
2
|
+
import { dirname, join, relative } from 'node:path';
|
|
3
|
+
import { tmpdir } from 'node:os';
|
|
4
|
+
import { loadDesignContext } from '../context.js';
|
|
5
|
+
import { appendApprovals, appendToolkitArtifacts, ensureToolkitState, loadDesignRunState, saveDesignRunState, upsertStage, upsertToolkitContext } from '../state.js';
|
|
6
|
+
import { stageArtifacts } from '../../../../orchestrator/src/persistence/ArtifactStager.js';
|
|
7
|
+
import { buildRetentionMetadata, computeToolkitRetention, ensureSourcePermitted, loadToolkitPermit, resolveToolkitSources, slugifyToolkitValue } from './common.js';
|
|
8
|
+
import { capturePageSnapshot } from './snapshot.js';
|
|
9
|
+
async function main() {
|
|
10
|
+
const context = await loadDesignContext();
|
|
11
|
+
const state = await loadDesignRunState(context.statePath);
|
|
12
|
+
const stageId = 'design-toolkit-extract';
|
|
13
|
+
const toolkitState = ensureToolkitState(state);
|
|
14
|
+
const metadata = context.config.config.metadata.design;
|
|
15
|
+
const pipelineConfig = context.config.config.pipelines.hiFiDesignToolkit;
|
|
16
|
+
const sources = resolveToolkitSources(pipelineConfig, metadata);
|
|
17
|
+
if (sources.length === 0) {
|
|
18
|
+
upsertStage(state, {
|
|
19
|
+
id: stageId,
|
|
20
|
+
title: 'Toolkit context acquisition',
|
|
21
|
+
status: 'skipped',
|
|
22
|
+
notes: ['No hi-fi design toolkit sources configured.']
|
|
23
|
+
});
|
|
24
|
+
await saveDesignRunState(context.statePath, state);
|
|
25
|
+
console.log('[design-toolkit-extract] skipped — no sources configured');
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
const permit = await loadToolkitPermit(context.repoRoot).catch((error) => {
|
|
29
|
+
console.warn('[design-toolkit-extract] permit load failed, proceeding without enforcement', error);
|
|
30
|
+
return { allowedSources: [] };
|
|
31
|
+
});
|
|
32
|
+
const fallbackRetention = {
|
|
33
|
+
days: state.retention?.days ?? metadata.retention.days,
|
|
34
|
+
autoPurge: state.retention?.autoPurge ?? metadata.retention.autoPurge,
|
|
35
|
+
policy: state.retention?.policy ?? 'design.config.retention'
|
|
36
|
+
};
|
|
37
|
+
toolkitState.retention = computeToolkitRetention(pipelineConfig, fallbackRetention);
|
|
38
|
+
const now = new Date();
|
|
39
|
+
const tmpRoot = join(tmpdir(), `design-toolkit-context-${Date.now()}`);
|
|
40
|
+
await mkdir(tmpRoot, { recursive: true });
|
|
41
|
+
let successCount = 0;
|
|
42
|
+
const failures = [];
|
|
43
|
+
const stagedArtifacts = [];
|
|
44
|
+
const approvals = [];
|
|
45
|
+
for (const source of sources) {
|
|
46
|
+
try {
|
|
47
|
+
ensureSourcePermitted(source.url, permit);
|
|
48
|
+
const contextResult = await stageContextArtifact({
|
|
49
|
+
context,
|
|
50
|
+
source,
|
|
51
|
+
tmpRoot,
|
|
52
|
+
retentionDays: toolkitState.retention?.days ?? fallbackRetention.days,
|
|
53
|
+
retentionPolicy: toolkitState.retention?.policy ?? fallbackRetention.policy,
|
|
54
|
+
autoPurge: toolkitState.retention?.autoPurge ?? fallbackRetention.autoPurge,
|
|
55
|
+
timestamp: now,
|
|
56
|
+
liveAssets: pipelineConfig.liveAssets,
|
|
57
|
+
interactionsEnabled: pipelineConfig.interactions?.enabled ?? false
|
|
58
|
+
});
|
|
59
|
+
successCount += 1;
|
|
60
|
+
stagedArtifacts.push(contextResult.artifact);
|
|
61
|
+
approvals.push({
|
|
62
|
+
id: `playwright-${source.slug}`,
|
|
63
|
+
actor: metadata.privacy.approver ?? 'design-reviewer',
|
|
64
|
+
reason: `Playwright extraction approved for ${source.url}`,
|
|
65
|
+
timestamp: new Date().toISOString()
|
|
66
|
+
});
|
|
67
|
+
upsertToolkitContext(state, {
|
|
68
|
+
id: source.id ?? slugifyToolkitValue(source.url, successCount - 1),
|
|
69
|
+
slug: source.slug,
|
|
70
|
+
title: source.title ?? null,
|
|
71
|
+
url: source.url,
|
|
72
|
+
referenceUrl: source.referenceUrl ?? source.url,
|
|
73
|
+
relativeDir: contextResult.artifact.relative_path.split('/').slice(0, -1).join('/'),
|
|
74
|
+
breakpoints: source.breakpoints.map((bp) => bp.id),
|
|
75
|
+
snapshotHtmlPath: contextResult.paths.inlineHtmlPath,
|
|
76
|
+
snapshotRawHtmlPath: contextResult.paths.rawHtmlPath,
|
|
77
|
+
snapshotCssPath: contextResult.paths.stylesPath,
|
|
78
|
+
palettePath: contextResult.paths.palettePath,
|
|
79
|
+
sectionsPath: contextResult.paths.sectionsPath,
|
|
80
|
+
palettePreview: contextResult.palettePreview,
|
|
81
|
+
fontFamilies: contextResult.fontFamilies,
|
|
82
|
+
runtimeCanvasColors: contextResult.runtimeCanvasColors,
|
|
83
|
+
resolvedFonts: contextResult.resolvedFonts,
|
|
84
|
+
interactionScriptPath: pipelineConfig.interactions?.scriptPath ?? null,
|
|
85
|
+
interactionWaitMs: pipelineConfig.interactions?.waitMs ?? null
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
catch (error) {
|
|
89
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
90
|
+
failures.push(`${source.url}: ${message}`);
|
|
91
|
+
console.error(`[design-toolkit-extract] failed to process ${source.url}: ${message}`);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
if (stagedArtifacts.length > 0) {
|
|
95
|
+
appendToolkitArtifacts(state, stagedArtifacts);
|
|
96
|
+
}
|
|
97
|
+
if (approvals.length > 0) {
|
|
98
|
+
appendApprovals(state, approvals);
|
|
99
|
+
}
|
|
100
|
+
const stageStatus = failures.length === 0 && successCount > 0 ? 'succeeded' : 'failed';
|
|
101
|
+
upsertStage(state, {
|
|
102
|
+
id: stageId,
|
|
103
|
+
title: 'Toolkit context acquisition',
|
|
104
|
+
status: stageStatus,
|
|
105
|
+
notes: failures.length > 0 ? failures : undefined,
|
|
106
|
+
metrics: {
|
|
107
|
+
source_count: sources.length,
|
|
108
|
+
captured_count: successCount
|
|
109
|
+
},
|
|
110
|
+
artifacts: stagedArtifacts.map((artifact) => ({
|
|
111
|
+
relative_path: artifact.relative_path,
|
|
112
|
+
stage: 'extract',
|
|
113
|
+
status: artifact.status,
|
|
114
|
+
description: artifact.description ?? undefined
|
|
115
|
+
}))
|
|
116
|
+
});
|
|
117
|
+
await saveDesignRunState(context.statePath, state);
|
|
118
|
+
if (stageStatus === 'failed') {
|
|
119
|
+
throw new Error('One or more toolkit sources failed staging.');
|
|
120
|
+
}
|
|
121
|
+
console.log(`[design-toolkit-extract] staged ${successCount} / ${sources.length} sources`);
|
|
122
|
+
}
|
|
123
|
+
async function stageContextArtifact(options) {
|
|
124
|
+
const { context, source, tmpRoot, retentionDays, retentionPolicy, autoPurge, timestamp, liveAssets, interactionsEnabled } = options;
|
|
125
|
+
const primaryBreakpoint = source.breakpoints[0];
|
|
126
|
+
const viewport = primaryBreakpoint
|
|
127
|
+
? {
|
|
128
|
+
width: primaryBreakpoint.width,
|
|
129
|
+
height: primaryBreakpoint.height,
|
|
130
|
+
deviceScaleFactor: primaryBreakpoint.deviceScaleFactor
|
|
131
|
+
}
|
|
132
|
+
: undefined;
|
|
133
|
+
const snapshot = await capturePageSnapshot(source.url, {
|
|
134
|
+
keepScripts: liveAssets?.keepScripts ?? false,
|
|
135
|
+
maxStylesheets: liveAssets?.maxStylesheets ?? undefined,
|
|
136
|
+
mirrorAssets: liveAssets?.mirrorAssets ?? false,
|
|
137
|
+
allowRemoteAssets: liveAssets?.allowRemoteAssets ?? false,
|
|
138
|
+
viewport,
|
|
139
|
+
runInteractions: Boolean(interactionsEnabled)
|
|
140
|
+
});
|
|
141
|
+
const slugDir = join(tmpRoot, source.slug);
|
|
142
|
+
await mkdir(slugDir, { recursive: true });
|
|
143
|
+
const contextPayload = {
|
|
144
|
+
id: source.id,
|
|
145
|
+
url: source.url,
|
|
146
|
+
referenceUrl: source.referenceUrl,
|
|
147
|
+
breakpoints: source.breakpoints,
|
|
148
|
+
maskSelectors: source.maskSelectors,
|
|
149
|
+
capturedAt: timestamp.toISOString(),
|
|
150
|
+
colorPalettePreview: snapshot.colorPalette.slice(0, 12),
|
|
151
|
+
fontFamilies: snapshot.fontFamilies,
|
|
152
|
+
sectionCount: snapshot.sections.length,
|
|
153
|
+
runtimeCanvasColors: snapshot.runtimeCanvasColors,
|
|
154
|
+
resolvedFonts: snapshot.resolvedFonts
|
|
155
|
+
};
|
|
156
|
+
const contextPath = join(slugDir, 'context.json');
|
|
157
|
+
const inlineHtmlPath = join(slugDir, 'inline.html');
|
|
158
|
+
const rawHtmlPath = join(slugDir, 'original.html');
|
|
159
|
+
const stylesPath = join(slugDir, 'styles.css');
|
|
160
|
+
const palettePath = join(slugDir, 'palette.json');
|
|
161
|
+
const sectionsPath = join(slugDir, 'sections.json');
|
|
162
|
+
await Promise.all([
|
|
163
|
+
writeFile(contextPath, `${JSON.stringify(contextPayload, null, 2)}\n`, 'utf8'),
|
|
164
|
+
writeFile(inlineHtmlPath, snapshot.inlineHtml, 'utf8'),
|
|
165
|
+
writeFile(rawHtmlPath, snapshot.originalHtml, 'utf8'),
|
|
166
|
+
writeFile(stylesPath, snapshot.aggregatedCss, 'utf8'),
|
|
167
|
+
writeFile(palettePath, `${JSON.stringify(snapshot.colorPalette, null, 2)}\n`, 'utf8'),
|
|
168
|
+
writeFile(sectionsPath, `${JSON.stringify(snapshot.sections, null, 2)}\n`, 'utf8')
|
|
169
|
+
]);
|
|
170
|
+
if (snapshot.assets.length > 0) {
|
|
171
|
+
for (const asset of snapshot.assets) {
|
|
172
|
+
const assetPath = join(slugDir, asset.relativePath);
|
|
173
|
+
await mkdir(dirname(assetPath), { recursive: true });
|
|
174
|
+
await writeFile(assetPath, asset.buffer);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
const stagedFiles = await stageArtifacts({
|
|
178
|
+
taskId: context.taskId,
|
|
179
|
+
runId: context.runId,
|
|
180
|
+
artifacts: [
|
|
181
|
+
{
|
|
182
|
+
path: relative(process.cwd(), contextPath),
|
|
183
|
+
description: `Toolkit context for ${source.url}`
|
|
184
|
+
},
|
|
185
|
+
{
|
|
186
|
+
path: relative(process.cwd(), inlineHtmlPath),
|
|
187
|
+
description: `Inlined Cognition clone for ${source.url}`
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
path: relative(process.cwd(), rawHtmlPath),
|
|
191
|
+
description: `Original HTML for ${source.url}`
|
|
192
|
+
},
|
|
193
|
+
{
|
|
194
|
+
path: relative(process.cwd(), stylesPath),
|
|
195
|
+
description: `Aggregated CSS for ${source.url}`
|
|
196
|
+
},
|
|
197
|
+
{
|
|
198
|
+
path: relative(process.cwd(), palettePath),
|
|
199
|
+
description: `Color palette for ${source.url}`
|
|
200
|
+
},
|
|
201
|
+
{
|
|
202
|
+
path: relative(process.cwd(), sectionsPath),
|
|
203
|
+
description: `Section summaries for ${source.url}`
|
|
204
|
+
}
|
|
205
|
+
],
|
|
206
|
+
options: {
|
|
207
|
+
relativeDir: join('design-toolkit', 'context', source.slug),
|
|
208
|
+
overwrite: true
|
|
209
|
+
}
|
|
210
|
+
});
|
|
211
|
+
const [contextRecord, inlineRecord, rawRecord, stylesRecord, paletteRecord, sectionsRecord] = stagedFiles;
|
|
212
|
+
const contextDir = join(process.cwd(), dirname(contextRecord.path));
|
|
213
|
+
if (snapshot.assets.length > 0) {
|
|
214
|
+
for (const asset of snapshot.assets) {
|
|
215
|
+
const tempPath = join(slugDir, asset.relativePath);
|
|
216
|
+
const destinationPath = join(contextDir, asset.relativePath);
|
|
217
|
+
await mkdir(dirname(destinationPath), { recursive: true });
|
|
218
|
+
await copyFile(tempPath, destinationPath);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
const retention = buildRetentionMetadata({
|
|
222
|
+
days: retentionDays,
|
|
223
|
+
autoPurge,
|
|
224
|
+
policy: retentionPolicy
|
|
225
|
+
}, timestamp);
|
|
226
|
+
return {
|
|
227
|
+
artifact: {
|
|
228
|
+
id: source.id,
|
|
229
|
+
stage: 'extract',
|
|
230
|
+
status: 'succeeded',
|
|
231
|
+
relative_path: contextRecord.path,
|
|
232
|
+
description: `Context snapshot for ${source.slug}`,
|
|
233
|
+
retention,
|
|
234
|
+
metrics: {
|
|
235
|
+
breakpoint_count: source.breakpoints.length,
|
|
236
|
+
color_count: snapshot.colorPalette.length,
|
|
237
|
+
section_count: snapshot.sections.length
|
|
238
|
+
}
|
|
239
|
+
},
|
|
240
|
+
paths: {
|
|
241
|
+
contextPath: contextRecord.path,
|
|
242
|
+
inlineHtmlPath: inlineRecord.path,
|
|
243
|
+
rawHtmlPath: rawRecord.path,
|
|
244
|
+
stylesPath: stylesRecord.path,
|
|
245
|
+
palettePath: paletteRecord.path,
|
|
246
|
+
sectionsPath: sectionsRecord.path
|
|
247
|
+
},
|
|
248
|
+
palettePreview: snapshot.colorPalette.slice(0, 12),
|
|
249
|
+
fontFamilies: snapshot.fontFamilies,
|
|
250
|
+
runtimeCanvasColors: snapshot.runtimeCanvasColors,
|
|
251
|
+
resolvedFonts: snapshot.resolvedFonts
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
main().catch((error) => {
|
|
255
|
+
console.error('[design-toolkit-extract] failed to stage toolkit context');
|
|
256
|
+
console.error(error instanceof Error ? error.stack ?? error.message : error);
|
|
257
|
+
process.exitCode = 1;
|
|
258
|
+
});
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
import { mkdir, readFile, writeFile } from 'node:fs/promises';
|
|
2
|
+
import { join, relative } from 'node:path';
|
|
3
|
+
import { tmpdir } from 'node:os';
|
|
4
|
+
import { spawn } from 'node:child_process';
|
|
5
|
+
import { loadDesignContext } from '../context.js';
|
|
6
|
+
import { appendToolkitArtifacts, ensureToolkitState, loadDesignRunState, saveDesignRunState, upsertStage } from '../state.js';
|
|
7
|
+
import { stageArtifacts } from '../../../../orchestrator/src/persistence/ArtifactStager.js';
|
|
8
|
+
import { buildRetentionMetadata } from './common.js';
|
|
9
|
+
import { resolveToolkitPublishActions } from './publishActions.js';
|
|
10
|
+
const DESIGN_SYSTEM_DIR = 'packages/design-system';
|
|
11
|
+
async function main() {
|
|
12
|
+
const context = await loadDesignContext();
|
|
13
|
+
const state = await loadDesignRunState(context.statePath);
|
|
14
|
+
const stageId = 'design-toolkit-publish';
|
|
15
|
+
const toolkitState = ensureToolkitState(state);
|
|
16
|
+
const contexts = toolkitState.contexts.filter((entry) => entry.tokensPath);
|
|
17
|
+
const pipelineConfig = context.config.config.pipelines.hiFiDesignToolkit;
|
|
18
|
+
const publishActions = resolveToolkitPublishActions(pipelineConfig);
|
|
19
|
+
const hasAnyAction = publishActions.updateTokens || publishActions.updateComponents || publishActions.runVisualRegression;
|
|
20
|
+
if ((publishActions.updateTokens || publishActions.updateComponents) && contexts.length === 0) {
|
|
21
|
+
upsertStage(state, {
|
|
22
|
+
id: stageId,
|
|
23
|
+
title: 'Toolkit publish + design-system integration',
|
|
24
|
+
status: 'skipped',
|
|
25
|
+
notes: ['No token artifacts available. Run earlier stages first.']
|
|
26
|
+
});
|
|
27
|
+
await saveDesignRunState(context.statePath, state);
|
|
28
|
+
console.log('[design-toolkit-publish] skipped — no token artifacts');
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
if (!hasAnyAction) {
|
|
32
|
+
upsertStage(state, {
|
|
33
|
+
id: stageId,
|
|
34
|
+
title: 'Toolkit publish + design-system integration',
|
|
35
|
+
status: 'skipped',
|
|
36
|
+
notes: ['publish.update_* config disabled all publish actions.']
|
|
37
|
+
});
|
|
38
|
+
await saveDesignRunState(context.statePath, state);
|
|
39
|
+
console.log('[design-toolkit-publish] skipped — publish actions disabled via config');
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
const retention = toolkitState.retention ?? {
|
|
43
|
+
days: state.retention?.days ?? 30,
|
|
44
|
+
autoPurge: state.retention?.autoPurge ?? false,
|
|
45
|
+
policy: state.retention?.policy ?? 'design.config.retention'
|
|
46
|
+
};
|
|
47
|
+
const repoRoot = context.repoRoot;
|
|
48
|
+
const tokensTarget = join(repoRoot, DESIGN_SYSTEM_DIR, 'tokens', 'src', 'hi-fi');
|
|
49
|
+
const componentTarget = join(repoRoot, DESIGN_SYSTEM_DIR, 'src', 'components', 'hi-fi');
|
|
50
|
+
const dirPromises = [];
|
|
51
|
+
if (publishActions.updateTokens) {
|
|
52
|
+
dirPromises.push(mkdir(tokensTarget, { recursive: true }).then(() => undefined));
|
|
53
|
+
}
|
|
54
|
+
if (publishActions.updateComponents) {
|
|
55
|
+
dirPromises.push(mkdir(componentTarget, { recursive: true }).then(() => undefined));
|
|
56
|
+
}
|
|
57
|
+
await Promise.all(dirPromises);
|
|
58
|
+
let tokensWritten = 0;
|
|
59
|
+
let componentsWritten = 0;
|
|
60
|
+
if (publishActions.updateTokens || publishActions.updateComponents) {
|
|
61
|
+
for (const entry of contexts) {
|
|
62
|
+
const sourcePath = join(repoRoot, entry.tokensPath);
|
|
63
|
+
if (publishActions.updateTokens) {
|
|
64
|
+
const destPath = join(tokensTarget, `${entry.slug}.json`);
|
|
65
|
+
const tokensRaw = await readFile(sourcePath, 'utf8');
|
|
66
|
+
const tokens = JSON.parse(tokensRaw);
|
|
67
|
+
tokens.metadata = {
|
|
68
|
+
...(tokens.metadata ?? {}),
|
|
69
|
+
manifest: context.manifestPath
|
|
70
|
+
};
|
|
71
|
+
await writeFile(destPath, `${JSON.stringify(tokens, null, 2)}\n`, 'utf8');
|
|
72
|
+
tokensWritten += 1;
|
|
73
|
+
}
|
|
74
|
+
if (publishActions.updateComponents) {
|
|
75
|
+
const componentDir = join(componentTarget, entry.slug);
|
|
76
|
+
await mkdir(componentDir, { recursive: true });
|
|
77
|
+
const componentName = toPascalCase(entry.slug);
|
|
78
|
+
const componentFile = join(componentDir, 'index.ts');
|
|
79
|
+
const componentSource = `export const ${componentName} = {\n id: '${entry.slug}',\n tokensFile: '../../tokens/src/hi-fi/${entry.slug}.json',\n referencePath: '${entry.referencePath ?? ''}'\n};\n`;
|
|
80
|
+
await writeFile(componentFile, componentSource, 'utf8');
|
|
81
|
+
componentsWritten += 1;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
const commandResults = await runDesignSystemCommands(repoRoot, publishActions);
|
|
86
|
+
const tmpRoot = join(tmpdir(), `design-toolkit-publish-${Date.now()}`);
|
|
87
|
+
await mkdir(tmpRoot, { recursive: true });
|
|
88
|
+
const logPath = join(tmpRoot, 'publish-summary.json');
|
|
89
|
+
await writeFile(logPath, JSON.stringify(commandResults, null, 2), 'utf8');
|
|
90
|
+
const [stagedLog] = await stageArtifacts({
|
|
91
|
+
taskId: context.taskId,
|
|
92
|
+
runId: context.runId,
|
|
93
|
+
artifacts: [
|
|
94
|
+
{
|
|
95
|
+
path: relative(process.cwd(), logPath),
|
|
96
|
+
description: 'Design-system publish logs'
|
|
97
|
+
}
|
|
98
|
+
],
|
|
99
|
+
options: {
|
|
100
|
+
relativeDir: 'design-toolkit/publish/logs',
|
|
101
|
+
overwrite: true
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
const retentionMetadata = buildRetentionMetadata(retention, new Date());
|
|
105
|
+
const artifact = {
|
|
106
|
+
id: 'design-system-publish',
|
|
107
|
+
stage: 'publish',
|
|
108
|
+
status: commandResults.failed === 0 ? 'succeeded' : 'failed',
|
|
109
|
+
relative_path: stagedLog.path,
|
|
110
|
+
description: 'Design-system integration summary',
|
|
111
|
+
retention: retentionMetadata,
|
|
112
|
+
metrics: {
|
|
113
|
+
contexts_updated: Math.max(tokensWritten, componentsWritten),
|
|
114
|
+
tokens_written: tokensWritten,
|
|
115
|
+
components_written: componentsWritten,
|
|
116
|
+
commands_passed: commandResults.passed,
|
|
117
|
+
commands_failed: commandResults.failed,
|
|
118
|
+
commands_executed: commandResults.executed.length
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
appendToolkitArtifacts(state, [artifact]);
|
|
122
|
+
const stageNotes = [];
|
|
123
|
+
if (!publishActions.updateTokens) {
|
|
124
|
+
stageNotes.push('Token publishing disabled via config.');
|
|
125
|
+
}
|
|
126
|
+
if (!publishActions.updateComponents) {
|
|
127
|
+
stageNotes.push('Component publishing disabled via config.');
|
|
128
|
+
}
|
|
129
|
+
if (!publishActions.runVisualRegression) {
|
|
130
|
+
stageNotes.push('Visual regression command disabled via config.');
|
|
131
|
+
}
|
|
132
|
+
upsertStage(state, {
|
|
133
|
+
id: stageId,
|
|
134
|
+
title: 'Toolkit publish + design-system integration',
|
|
135
|
+
status: artifact.status === 'succeeded' ? 'succeeded' : 'failed',
|
|
136
|
+
notes: commandResults.failed > 0
|
|
137
|
+
? [...stageNotes, ...commandResults.errors]
|
|
138
|
+
: stageNotes.length > 0
|
|
139
|
+
? stageNotes
|
|
140
|
+
: undefined,
|
|
141
|
+
metrics: artifact.metrics ?? undefined,
|
|
142
|
+
artifacts: [
|
|
143
|
+
{
|
|
144
|
+
relative_path: stagedLog.path,
|
|
145
|
+
stage: 'publish',
|
|
146
|
+
status: artifact.status,
|
|
147
|
+
description: 'publish-summary.json'
|
|
148
|
+
}
|
|
149
|
+
]
|
|
150
|
+
});
|
|
151
|
+
await saveDesignRunState(context.statePath, state);
|
|
152
|
+
if (artifact.status === 'failed') {
|
|
153
|
+
throw new Error('One or more design-system commands failed.');
|
|
154
|
+
}
|
|
155
|
+
console.log('[design-toolkit-publish] updated design-system tokens/components');
|
|
156
|
+
}
|
|
157
|
+
async function runDesignSystemCommands(repoRoot, actions) {
|
|
158
|
+
const commands = [];
|
|
159
|
+
if (actions.updateTokens) {
|
|
160
|
+
commands.push({ args: ['npm', '--prefix', DESIGN_SYSTEM_DIR, 'run', 'build:tokens'], label: 'build:tokens' });
|
|
161
|
+
}
|
|
162
|
+
if (actions.updateComponents) {
|
|
163
|
+
commands.push({ args: ['npm', '--prefix', DESIGN_SYSTEM_DIR, 'run', 'lint'], label: 'lint' });
|
|
164
|
+
}
|
|
165
|
+
if (actions.runVisualRegression) {
|
|
166
|
+
commands.push({ args: ['npm', '--prefix', DESIGN_SYSTEM_DIR, 'run', 'test:visual'], label: 'test:visual' });
|
|
167
|
+
}
|
|
168
|
+
const errors = [];
|
|
169
|
+
const executed = [];
|
|
170
|
+
let passed = 0;
|
|
171
|
+
let failed = 0;
|
|
172
|
+
for (const command of commands) {
|
|
173
|
+
const exitCode = await spawnCommand(command.args, repoRoot);
|
|
174
|
+
executed.push(command.label);
|
|
175
|
+
if (exitCode === 0) {
|
|
176
|
+
passed += 1;
|
|
177
|
+
}
|
|
178
|
+
else {
|
|
179
|
+
failed += 1;
|
|
180
|
+
errors.push(`${command.label} failed with exit code ${exitCode}`);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
return { passed, failed, errors, executed };
|
|
184
|
+
}
|
|
185
|
+
function spawnCommand(args, cwd) {
|
|
186
|
+
return new Promise((resolve) => {
|
|
187
|
+
const child = spawn(args[0], args.slice(1), { stdio: 'inherit', cwd });
|
|
188
|
+
child.on('close', (code) => resolve(code ?? 1));
|
|
189
|
+
child.on('error', () => resolve(1));
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
function toPascalCase(value) {
|
|
193
|
+
return value
|
|
194
|
+
.split(/[-_\s]+/)
|
|
195
|
+
.map((segment) => segment.charAt(0).toUpperCase() + segment.slice(1))
|
|
196
|
+
.join('');
|
|
197
|
+
}
|
|
198
|
+
main().catch((error) => {
|
|
199
|
+
console.error('[design-toolkit-publish] failed to publish toolkit outputs');
|
|
200
|
+
console.error(error instanceof Error ? error.stack ?? error.message : error);
|
|
201
|
+
process.exitCode = 1;
|
|
202
|
+
});
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export function resolveToolkitPublishActions(pipeline) {
|
|
2
|
+
const publish = pipeline.publish ?? {
|
|
3
|
+
updateTokens: true,
|
|
4
|
+
updateComponents: true,
|
|
5
|
+
runVisualRegression: true
|
|
6
|
+
};
|
|
7
|
+
return {
|
|
8
|
+
updateTokens: publish.updateTokens !== false,
|
|
9
|
+
updateComponents: publish.updateComponents !== false,
|
|
10
|
+
runVisualRegression: publish.runVisualRegression !== false
|
|
11
|
+
};
|
|
12
|
+
}
|