stylegallery 0.1.0 → 0.1.2
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/AGENTS.md +4 -1
- package/DOMAINS.md +2 -6
- package/GOVERNANCE.md +10 -9
- package/README.md +29 -127
- package/consumer-reference/agent-native/v2/admission-policy.json +646 -0
- package/consumer-reference/agent-native/v2/material-registry.json +1 -0
- package/consumer-reference/agent-native/v2/schema/admission-policy.schema.json +50 -0
- package/consumer-reference/agent-native/v2/schema/material-manifest.schema.json +17 -0
- package/consumer-reference/agent-native/v2/schema/material-record.schema.json +20 -0
- package/consumer-reference/contract.md +1 -1
- package/consumer-reference/index.md +5 -3
- package/layout/index.md +124 -0
- package/log.md +57 -0
- package/package.json +183 -22
- package/scripts/agent-native/v2/experimental-extension-registry.mjs +143 -0
- package/scripts/agent-native/v2/extensions/a2a-projection.mjs +264 -0
- package/scripts/agent-native/v2/extensions/agui-projection.mjs +191 -0
- package/scripts/agent-native/v2/material-admission.mjs +295 -0
- package/scripts/agent-native/v2/material-cli-adapter.mjs +134 -0
- package/scripts/agent-native/v2/material-context.mjs +251 -0
- package/scripts/agent-native/v2/material-identity.mjs +71 -0
- package/scripts/agent-native/v2/material-mcp-adapter.mjs +253 -0
- package/scripts/agent-native/v2/material-operation-registry.mjs +123 -0
- package/scripts/agent-native/v2/material-queries.mjs +398 -0
- package/scripts/agent-native/v2/material-registry.mjs +319 -0
- package/scripts/sg-material-mcp.mjs +98 -0
- package/scripts/sg-material.mjs +27 -0
- package/consumer-reference/adapters/style-dictionary.config.mjs +0 -36
- package/consumer-reference/baselines/calibration.json +0 -260
- package/consumer-reference/baselines/manifest.json +0 -21
- package/consumer-reference/fixtures/consumer-conformance/e2e-fixture.mjs +0 -282
- package/consumer-reference/fixtures/consumer-conformance/manifest.json +0 -102
- package/consumer-reference/fixtures/consumer-conformance/runtime-proof.mjs +0 -32
- package/consumer-reference/fixtures/consumer-conformance/valid-migration-result.json +0 -18
- package/consumer-reference/fixtures/consumer-conformance/valid-migration.json +0 -142
- package/consumer-reference/fixtures/invalid-stable-ended-support.json +0 -19
- package/consumer-reference/fixtures/promotion/invalid-blocking-evidence.json +0 -26
- package/consumer-reference/fixtures/promotion/invalid-count-only.json +0 -29
- package/consumer-reference/fixtures/promotion/invalid-missing-owner-migration.json +0 -25
- package/consumer-reference/fixtures/promotion/invalid-normative-bypass-no-regression.json +0 -25
- package/consumer-reference/fixtures/promotion/invalid-related-counted-independent.json +0 -45
- package/consumer-reference/fixtures/promotion/invalid-stable-ended-support.json +0 -26
- package/consumer-reference/fixtures/promotion/manifest.json +0 -15
- package/consumer-reference/fixtures/promotion/valid-deferred-example.json +0 -67
- package/consumer-reference/fixtures/promotion/valid-normative-bypass.json +0 -67
- package/consumer-reference/fixtures/token-portability/invalid-cycle.json +0 -7
- package/consumer-reference/fixtures/token-portability/invalid-dangling-alias.json +0 -6
- package/consumer-reference/fixtures/token-portability/invalid-extends.json +0 -4
- package/consumer-reference/fixtures/token-portability/invalid-json-pointer.json +0 -7
- package/consumer-reference/fixtures/token-portability/invalid-resolver.json +0 -9
- package/consumer-reference/fixtures/token-portability/invalid-type-mismatch.json +0 -10
- package/consumer-reference/fixtures/token-portability/invalid-unknown-reserved.json +0 -6
- package/consumer-reference/fixtures/token-portability/invalid-untested-type.json +0 -6
- package/consumer-reference/fixtures/token-portability/valid-border.json +0 -14
- package/consumer-reference/fixtures/token-portability/valid-reference.json +0 -32
- package/consumer-reference/fixtures/token-portability/valid-whole-alias.json +0 -7
- package/consumer-reference/fixtures/valid-deprecated.json +0 -21
- package/consumer-reference/fixtures/valid-experimental.json +0 -19
- package/consumer-reference/generated/manifest.json +0 -40
- package/consumer-reference/generated/tokens.css +0 -12
- package/consumer-reference/policies/shared-experimental.json +0 -37
- package/consumer-reference/schema/ax-evidence.schema.json +0 -30
- package/consumer-reference/schema/baseline-manifest.schema.json +0 -55
- package/consumer-reference/schema/calibration-record.schema.json +0 -190
- package/consumer-reference/schema/capture-session.schema.json +0 -161
- package/consumer-reference/schema/component-state.schema.json +0 -150
- package/consumer-reference/schema/consumer-conformance-record.schema.json +0 -365
- package/consumer-reference/schema/dom-evidence.schema.json +0 -38
- package/consumer-reference/schema/evidence-record.schema.json +0 -81
- package/consumer-reference/schema/fixture-manifest.schema.json +0 -32
- package/consumer-reference/schema/item.schema.json +0 -188
- package/consumer-reference/schema/page-evidence-manifest.schema.json +0 -216
- package/consumer-reference/schema/page-evidence-session.schema.json +0 -119
- package/consumer-reference/schema/portable-tokens.schema.json +0 -104
- package/consumer-reference/schema/promotion-rfc.schema.json +0 -219
- package/consumer-reference/schema/runtime-evidence-manifest.schema.json +0 -33
- package/consumer-reference/schema/visual-evidence.schema.json +0 -31
- package/design-engineering/consumer-migration-readiness.md +0 -124
- package/design-engineering/interface-craft.md +0 -107
- package/design-engineering/reference-profiles/external-adaptation/index.md +0 -12
- package/design-engineering/reference-profiles/governed-local/editorial/components/button.component.json +0 -125
- package/design-engineering/reference-profiles/governed-local/editorial/evidence/button.evidence.json +0 -3504
- package/design-engineering/reference-profiles/governed-local/editorial/fixtures/button.fixture.json +0 -65
- package/design-engineering/reference-profiles/governed-local/editorial/generated/evidence-coverage.md +0 -23
- package/design-engineering/reference-profiles/governed-local/editorial/generated/keyboard-matrix.md +0 -19
- package/design-engineering/reference-profiles/governed-local/editorial/generated/state-matrix.md +0 -21
- package/design-engineering/reference-profiles/governed-local/editorial/local-foundations.json +0 -12
- package/design-engineering/reference-profiles/governed-local/editorial/states/button.states.json +0 -195
- package/design-engineering/reference-profiles/governed-local/editorial/tokens.dtcg.json +0 -17
- package/design-engineering/reference-profiles/governed-local/terminal/components/button.component.json +0 -125
- package/design-engineering/reference-profiles/governed-local/terminal/evidence/button.evidence.json +0 -3504
- package/design-engineering/reference-profiles/governed-local/terminal/fixtures/button.fixture.json +0 -65
- package/design-engineering/reference-profiles/governed-local/terminal/generated/evidence-coverage.md +0 -23
- package/design-engineering/reference-profiles/governed-local/terminal/generated/keyboard-matrix.md +0 -19
- package/design-engineering/reference-profiles/governed-local/terminal/generated/state-matrix.md +0 -21
- package/design-engineering/reference-profiles/governed-local/terminal/local-foundations.json +0 -12
- package/design-engineering/reference-profiles/governed-local/terminal/profile.json +0 -43
- package/design-engineering/reference-profiles/governed-local/terminal/states/button.states.json +0 -195
- package/design-engineering/reference-profiles/governed-local/terminal/tokens.dtcg.json +0 -17
- package/game-ui/classification.md +0 -82
- package/game-ui/reference-record.md +0 -62
- package/game-ui/screen-hierarchy.md +0 -107
- package/game-ui/unity/architecture.md +0 -166
- package/game-ui/unity/cli-loop.md +0 -84
- package/game-ui/unity/data/unity-technologies-public-repositories.json +0 -26654
- package/game-ui/unity/org-wiki.md +0 -146
- package/game-ui/unity/repository-map.md +0 -102
- package/game-ui/unity/ui-systems.md +0 -85
- package/motion/practice-reference.md +0 -145
- package/motion/review-workflow.md +0 -86
- package/motion/vocabulary.md +0 -82
- package/platform-guides/apple-interaction.md +0 -89
- package/scripts/agent-native/events.mjs +0 -113
- package/scripts/agent-native/immutable-store.mjs +0 -102
- package/scripts/artifact-metadata.mjs +0 -20
- package/scripts/audit-evidence-freshness.mjs +0 -308
- package/scripts/baseline-contract.mjs +0 -189
- package/scripts/baseline-schema-parity.mjs +0 -40
- package/scripts/build-reference-artifacts.mjs +0 -127
- package/scripts/calibration-raw-contract.mjs +0 -172
- package/scripts/capture-session-contract.mjs +0 -187
- package/scripts/component-state-artifact-fixture.mjs +0 -44
- package/scripts/component-state-contract.mjs +0 -99
- package/scripts/component-state-semantics.mjs +0 -128
- package/scripts/component-state-workflow-contract.mjs +0 -67
- package/scripts/component-state-workflow-negative-cases.mjs +0 -64
- package/scripts/consumer-conformance-contract.mjs +0 -198
- package/scripts/consumer-evidence-governance-contract.mjs +0 -210
- package/scripts/consumer-reference-case-runner.mjs +0 -93
- package/scripts/consumer-reference-schema.mjs +0 -155
- package/scripts/create-component-state-session.mjs +0 -100
- package/scripts/create-page-evidence-session.mjs +0 -139
- package/scripts/domain-document-boundaries.mjs +0 -40
- package/scripts/domain-validation-checks.mjs +0 -141
- package/scripts/evidence-artifact-contract.mjs +0 -191
- package/scripts/finalize-component-state-evidence.mjs +0 -165
- package/scripts/finalize-page-evidence.mjs +0 -197
- package/scripts/generate-consumer-reference-evidence.mjs +0 -154
- package/scripts/generate-patterns.mjs +0 -218
- package/scripts/governance-matrix-contract.mjs +0 -73
- package/scripts/governance-matrix-negative-cases.mjs +0 -114
- package/scripts/governance-policy-contract.mjs +0 -49
- package/scripts/governance-test-fixture.mjs +0 -321
- package/scripts/governance-workflow-negative-cases.mjs +0 -241
- package/scripts/governed-profile-registry.mjs +0 -125
- package/scripts/json-schema-formats.mjs +0 -31
- package/scripts/markdown-structure.mjs +0 -45
- package/scripts/page-artifact-metadata.mjs +0 -124
- package/scripts/page-evidence-contract.mjs +0 -297
- package/scripts/page-evidence-fixture.mjs +0 -271
- package/scripts/pattern-data.mjs +0 -325
- package/scripts/profile-record-contract.mjs +0 -140
- package/scripts/promotion-attestation-contract.mjs +0 -91
- package/scripts/promotion-boundary-test-contract.mjs +0 -92
- package/scripts/promotion-fixture-inventory.mjs +0 -109
- package/scripts/promotion-governance-contract.mjs +0 -28
- package/scripts/reference-artifact-path-contract.mjs +0 -28
- package/scripts/reference-profile-contract.mjs +0 -114
- package/scripts/reference-token-contract.mjs +0 -156
- package/scripts/run-consumer-page-evidence-ci.mjs +0 -170
- package/scripts/summarize-sentinel-calibration.mjs +0 -98
- package/scripts/test-agent-adapter-conformance.mjs +0 -250
- package/scripts/test-agent-conformance-receipt.mjs +0 -106
- package/scripts/test-agent-events.mjs +0 -81
- package/scripts/test-agent-execution-effects.mjs +0 -114
- package/scripts/test-agent-execution-fixtures.mjs +0 -140
- package/scripts/test-agent-execution-kernel.mjs +0 -39
- package/scripts/test-agent-execution-learning.mjs +0 -130
- package/scripts/test-agent-execution-lifecycle.mjs +0 -143
- package/scripts/test-agent-execution-safety.mjs +0 -158
- package/scripts/test-agent-execution-views.mjs +0 -117
- package/scripts/test-agent-identity-integrity-cases.mjs +0 -160
- package/scripts/test-agent-knowledge-kernel.mjs +0 -183
- package/scripts/test-agent-learning.mjs +0 -246
- package/scripts/test-agent-mcp.mjs +0 -246
- package/scripts/test-agent-native-schemas.mjs +0 -215
- package/scripts/test-agent-projections.mjs +0 -249
- package/scripts/test-agent-registry-mcp-integrity.mjs +0 -215
- package/scripts/test-agent-registry-mutations.mjs +0 -110
- package/scripts/test-audit-evidence-freshness.mjs +0 -177
- package/scripts/test-component-state-sentinel.mjs +0 -35
- package/scripts/test-component-state-source-contract.mjs +0 -83
- package/scripts/test-consumer-conformance-sentinel.mjs +0 -165
- package/scripts/test-consumer-reference-sentinel.mjs +0 -31
- package/scripts/test-generate-consumer-reference-evidence.mjs +0 -124
- package/scripts/test-json-cli-output.mjs +0 -151
- package/scripts/test-json-schema-formats.mjs +0 -40
- package/scripts/test-reference-adapters.mjs +0 -199
- package/scripts/test-renderer-purity.mjs +0 -34
- package/scripts/test-sg-cli.mjs +0 -129
- package/scripts/test-summarize-sentinel-calibration.mjs +0 -226
- package/scripts/test-validate-baseline-manifest.mjs +0 -234
- package/scripts/test-validate-component-state-artifacts.mjs +0 -243
- package/scripts/test-validate-component-state.mjs +0 -106
- package/scripts/test-validate-consumer-conformance.mjs +0 -427
- package/scripts/test-validate-consumer-reference-cli.mjs +0 -69
- package/scripts/test-validate-consumer-reference.mjs +0 -263
- package/scripts/test-validate-domains.mjs +0 -357
- package/scripts/test-validate-governance.mjs +0 -292
- package/scripts/test-validate-ia.mjs +0 -262
- package/scripts/test-validate-okf.mjs +0 -73
- package/scripts/test-validate-page-evidence.mjs +0 -302
- package/scripts/test-validate-patterns.mjs +0 -157
- package/scripts/test-validate-promotion-rfc.mjs +0 -250
- package/scripts/test-validate-reference-profiles.mjs +0 -170
- package/scripts/test-validate-unity-org-wiki.mjs +0 -57
- package/scripts/test-validate-unity-source-contracts.mjs +0 -67
- package/scripts/test-validate-vocabulary.mjs +0 -174
- package/scripts/test-validate-webpage-workflow.mjs +0 -293
- package/scripts/validate-baseline-manifest.mjs +0 -44
- package/scripts/validate-catalog.mjs +0 -65
- package/scripts/validate-component-state.mjs +0 -141
- package/scripts/validate-consumer-conformance.mjs +0 -477
- package/scripts/validate-consumer-reference.mjs +0 -288
- package/scripts/validate-domains.mjs +0 -237
- package/scripts/validate-governance.mjs +0 -248
- package/scripts/validate-ia.mjs +0 -136
- package/scripts/validate-links.mjs +0 -66
- package/scripts/validate-okf.mjs +0 -104
- package/scripts/validate-page-evidence.mjs +0 -191
- package/scripts/validate-patterns.mjs +0 -246
- package/scripts/validate-promotion-rfc.mjs +0 -224
- package/scripts/validate-reference-artifacts.mjs +0 -125
- package/scripts/validate-renderer-purity.mjs +0 -56
- package/scripts/validate-unity-org-wiki.mjs +0 -146
- package/scripts/validate-unity-source-contracts.mjs +0 -107
- package/scripts/validate-vocabulary.mjs +0 -115
- package/scripts/validate-webpage-workflow.mjs +0 -254
- package/scripts/visual-expectation-contract.mjs +0 -20
- package/scripts/workflow-action-contract.mjs +0 -163
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
import { createHash, randomBytes } from "node:crypto";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { spawnSync } from "node:child_process";
|
|
5
|
+
|
|
6
|
+
import { canonicalize, deepFreeze } from "../canonical-json.mjs";
|
|
7
|
+
import {
|
|
8
|
+
createMaterialManifest,
|
|
9
|
+
materialAdmissionPolicy,
|
|
10
|
+
materialStableRefForPath,
|
|
11
|
+
materialVersionIdForSource,
|
|
12
|
+
resolveMaterialRecord,
|
|
13
|
+
validateMaterialManifest,
|
|
14
|
+
} from "./material-admission.mjs";
|
|
15
|
+
|
|
16
|
+
export const MATERIAL_REGISTRY_PATH = "consumer-reference/agent-native/v2/material-registry.json";
|
|
17
|
+
const REGULAR_MODES = new Set(["100644", "100755"]);
|
|
18
|
+
const sha256 = (bytes) => createHash("sha256").update(bytes).digest("hex");
|
|
19
|
+
|
|
20
|
+
export class MaterialRegistryError extends TypeError {
|
|
21
|
+
constructor(code, message, sourcePath) {
|
|
22
|
+
super(message);
|
|
23
|
+
this.name = "MaterialRegistryError";
|
|
24
|
+
this.code = code;
|
|
25
|
+
if (sourcePath !== undefined) this.path = sourcePath;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function fail(code, message, sourcePath) { throw new MaterialRegistryError(code, message, sourcePath); }
|
|
30
|
+
function resultFailure(code, message, sourcePath) {
|
|
31
|
+
return deepFreeze({ ok: false, failures: [{ code, message, ...(sourcePath === undefined ? {} : { path: sourcePath }) }], materials: [] });
|
|
32
|
+
}
|
|
33
|
+
function assertOptions(options, allowed) {
|
|
34
|
+
if (!options || typeof options !== "object" || Array.isArray(options)) fail("material_registry_option_invalid", "options must be an object");
|
|
35
|
+
const unexpected = Object.keys(options).filter((key) => !allowed.has(key));
|
|
36
|
+
if (unexpected.length > 0) fail("material_registry_option_invalid", `unsupported option: ${unexpected[0]}`);
|
|
37
|
+
if (typeof options.repositoryRoot !== "string" || options.repositoryRoot.length === 0) fail("material_repository_invalid", "repositoryRoot must be a non-empty string");
|
|
38
|
+
}
|
|
39
|
+
function realpath(fileSystem, target) {
|
|
40
|
+
const implementation = fileSystem.realpathSync.native ?? fileSystem.realpathSync;
|
|
41
|
+
return implementation.call(fileSystem.realpathSync, target);
|
|
42
|
+
}
|
|
43
|
+
function repositoryState(repositoryRoot) {
|
|
44
|
+
const resolvedRoot = path.resolve(repositoryRoot);
|
|
45
|
+
let stats;
|
|
46
|
+
try { stats = fs.lstatSync(resolvedRoot); } catch (error) { fail("material_repository_invalid", `repository root is unavailable: ${error.message}`); }
|
|
47
|
+
if (!stats.isDirectory() || stats.isSymbolicLink()) fail("material_repository_invalid", "repository root must be a non-symlink directory");
|
|
48
|
+
return { resolvedRoot, canonicalRoot: realpath(fs, resolvedRoot) };
|
|
49
|
+
}
|
|
50
|
+
function trackedInventory(repositoryRoot) {
|
|
51
|
+
const result = spawnSync("git", ["-C", repositoryRoot, "ls-files", "-s", "-z", "--"], {
|
|
52
|
+
encoding: "buffer", maxBuffer: 16 * 1024 * 1024, timeout: 30_000,
|
|
53
|
+
});
|
|
54
|
+
if (result.status !== 0) fail("material_inventory_failed", result.error?.message ?? (result.stderr.toString("utf8").trim() || "git ls-files failed"));
|
|
55
|
+
const entries = new Map();
|
|
56
|
+
const text = result.stdout.subarray(0, result.stdout.at(-1) === 0 ? result.stdout.length - 1 : result.stdout.length).toString("utf8");
|
|
57
|
+
for (const encoded of text.split("\0").filter(Boolean)) {
|
|
58
|
+
const tab = encoded.indexOf("\t");
|
|
59
|
+
const metadata = encoded.slice(0, tab).split(" ");
|
|
60
|
+
if (tab < 0 || metadata.length !== 3) fail("material_inventory_failed", "Git inventory entry is malformed");
|
|
61
|
+
entries.set(encoded.slice(tab + 1), { mode: metadata[0], stage: metadata[2] });
|
|
62
|
+
}
|
|
63
|
+
return entries;
|
|
64
|
+
}
|
|
65
|
+
function inspectPath(root, repositoryPath) {
|
|
66
|
+
let current = root.resolvedRoot;
|
|
67
|
+
let stats;
|
|
68
|
+
try {
|
|
69
|
+
for (const component of repositoryPath.split("/")) {
|
|
70
|
+
current = path.join(current, component);
|
|
71
|
+
stats = fs.lstatSync(current);
|
|
72
|
+
if (stats.isSymbolicLink()) fail("material_path_symlink", "material path components must not be symlinks", repositoryPath);
|
|
73
|
+
}
|
|
74
|
+
} catch (error) {
|
|
75
|
+
if (error instanceof MaterialRegistryError) throw error;
|
|
76
|
+
fail("material_path_unavailable", `tracked material path is unavailable: ${error.message}`, repositoryPath);
|
|
77
|
+
}
|
|
78
|
+
if (!stats.isFile()) fail("material_path_not_regular", "material path must be a regular file", repositoryPath);
|
|
79
|
+
const canonicalTarget = realpath(fs, current);
|
|
80
|
+
const expectedTarget = path.resolve(root.canonicalRoot, ...repositoryPath.split("/"));
|
|
81
|
+
if (canonicalTarget !== expectedTarget || !canonicalTarget.startsWith(`${root.canonicalRoot}${path.sep}`)) {
|
|
82
|
+
fail("material_path_escape", "material canonical path escapes the repository root", repositoryPath);
|
|
83
|
+
}
|
|
84
|
+
return { target: current, canonicalTarget, dev: stats.dev, ino: stats.ino };
|
|
85
|
+
}
|
|
86
|
+
function readInspected(inspection, repositoryPath) {
|
|
87
|
+
let descriptor;
|
|
88
|
+
try {
|
|
89
|
+
descriptor = fs.openSync(inspection.target, fs.constants.O_RDONLY | (fs.constants.O_NOFOLLOW ?? 0));
|
|
90
|
+
const descriptorStats = fs.fstatSync(descriptor);
|
|
91
|
+
const currentStats = fs.lstatSync(inspection.target);
|
|
92
|
+
if (!descriptorStats.isFile() || currentStats.isSymbolicLink() || !currentStats.isFile()
|
|
93
|
+
|| descriptorStats.dev !== inspection.dev || descriptorStats.ino !== inspection.ino
|
|
94
|
+
|| currentStats.dev !== inspection.dev || currentStats.ino !== inspection.ino
|
|
95
|
+
|| realpath(fs, inspection.target) !== inspection.canonicalTarget) {
|
|
96
|
+
fail("material_path_race", "material path identity changed after preflight", repositoryPath);
|
|
97
|
+
}
|
|
98
|
+
return fs.readFileSync(descriptor);
|
|
99
|
+
} catch (error) {
|
|
100
|
+
if (error instanceof MaterialRegistryError) throw error;
|
|
101
|
+
const code = error?.code === "ELOOP" || error?.code === "ENOENT" ? "material_path_race" : "material_read_failed";
|
|
102
|
+
fail(code, `material could not be read safely: ${error.message}`, repositoryPath);
|
|
103
|
+
} finally {
|
|
104
|
+
if (descriptor !== undefined) fs.closeSync(descriptor);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function preflightAll(repositoryRoot) {
|
|
109
|
+
const root = repositoryState(repositoryRoot);
|
|
110
|
+
const inventory = trackedInventory(repositoryRoot);
|
|
111
|
+
const inspections = new Map();
|
|
112
|
+
for (const route of materialAdmissionPolicy.allowed_materials) {
|
|
113
|
+
const entry = inventory.get(route.repository_path);
|
|
114
|
+
if (!entry) fail("material_path_untracked", "material path is absent from tracked Git inventory", route.repository_path);
|
|
115
|
+
if (!REGULAR_MODES.has(entry.mode) || entry.stage !== "0") fail("material_git_mode_invalid", "material Git index entry must be a stage-zero regular blob", route.repository_path);
|
|
116
|
+
}
|
|
117
|
+
for (const route of materialAdmissionPolicy.allowed_materials) inspections.set(route.repository_path, inspectPath(root, route.repository_path));
|
|
118
|
+
return inspections;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export function generateMaterialRegistry(options) {
|
|
122
|
+
assertOptions(options, new Set(["repositoryRoot"]));
|
|
123
|
+
const inspections = preflightAll(options.repositoryRoot);
|
|
124
|
+
const materials = materialAdmissionPolicy.allowed_materials.map((route) => {
|
|
125
|
+
const bytes = readInspected(inspections.get(route.repository_path), route.repository_path);
|
|
126
|
+
const source_sha256 = sha256(bytes);
|
|
127
|
+
const stable_ref = materialStableRefForPath(route.repository_path);
|
|
128
|
+
return {
|
|
129
|
+
schema_version: "2.0",
|
|
130
|
+
record_kind: "material",
|
|
131
|
+
stable_ref,
|
|
132
|
+
version_id: materialVersionIdForSource(stable_ref, source_sha256),
|
|
133
|
+
repository_path: route.repository_path,
|
|
134
|
+
media_type: materialAdmissionPolicy.media_type,
|
|
135
|
+
source_sha256,
|
|
136
|
+
byte_length: bytes.byteLength,
|
|
137
|
+
lifecycle: route.lifecycle,
|
|
138
|
+
domain: route.domain,
|
|
139
|
+
};
|
|
140
|
+
});
|
|
141
|
+
const manifest = createMaterialManifest(materials);
|
|
142
|
+
const validation = validateMaterialManifest({ repositoryRoot: options.repositoryRoot, manifest });
|
|
143
|
+
if (!validation.ok) fail(validation.failures[0].code, validation.failures[0].message, validation.failures[0].path);
|
|
144
|
+
if (validation.materials.length !== materialAdmissionPolicy.allowed_materials.length) fail("material_registry_cardinality_invalid", "generated registry does not contain the sealed allow-set");
|
|
145
|
+
return manifest;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
export function validateMaterialRegistry(options) {
|
|
149
|
+
assertOptions(options, new Set(["repositoryRoot", "manifest"]));
|
|
150
|
+
if (!Object.hasOwn(options, "manifest")) return resultFailure("material_manifest_invalid", "manifest is required");
|
|
151
|
+
const result = validateMaterialManifest({ repositoryRoot: options.repositoryRoot, manifest: options.manifest });
|
|
152
|
+
if (!result.ok) return result;
|
|
153
|
+
if (result.materials.length !== materialAdmissionPolicy.allowed_materials.length) return resultFailure("material_registry_cardinality_invalid", "material registry must contain exactly the sealed allow-set");
|
|
154
|
+
const actual = new Set(result.materials.map(({ repository_path }) => repository_path));
|
|
155
|
+
const missing = materialAdmissionPolicy.allowed_materials.find(({ repository_path }) => !actual.has(repository_path));
|
|
156
|
+
if (missing) return resultFailure("material_registry_path_set_invalid", "material registry is missing an admitted policy path", missing.repository_path);
|
|
157
|
+
return result;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
export function loadMaterialRegistry(options) {
|
|
161
|
+
assertOptions(options, new Set(["repositoryRoot"]));
|
|
162
|
+
const target = path.join(path.resolve(options.repositoryRoot), ...MATERIAL_REGISTRY_PATH.split("/"));
|
|
163
|
+
let bytes;
|
|
164
|
+
try {
|
|
165
|
+
const stats = fs.lstatSync(target);
|
|
166
|
+
if (!stats.isFile() || stats.isSymbolicLink()) fail("material_registry_file_invalid", "material registry must be a regular non-symlink file", MATERIAL_REGISTRY_PATH);
|
|
167
|
+
bytes = fs.readFileSync(target);
|
|
168
|
+
} catch (error) {
|
|
169
|
+
if (error instanceof MaterialRegistryError) throw error;
|
|
170
|
+
fail("material_registry_file_invalid", `material registry is unavailable: ${error.message}`, MATERIAL_REGISTRY_PATH);
|
|
171
|
+
}
|
|
172
|
+
let manifest;
|
|
173
|
+
try { manifest = JSON.parse(bytes.toString("utf8")); } catch (error) { fail("material_registry_json_invalid", error.message, MATERIAL_REGISTRY_PATH); }
|
|
174
|
+
if (!bytes.equals(Buffer.from(canonicalize(manifest), "utf8"))) fail("material_registry_canonical_invalid", "material registry bytes must be canonical JSON", MATERIAL_REGISTRY_PATH);
|
|
175
|
+
const validation = validateMaterialRegistry({ repositoryRoot: options.repositoryRoot, manifest });
|
|
176
|
+
if (!validation.ok) fail(validation.failures[0].code, validation.failures[0].message, validation.failures[0].path);
|
|
177
|
+
return deepFreeze(manifest);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function inspectOutputParent(repositoryRoot, fileSystem) {
|
|
181
|
+
const root = repositoryState(repositoryRoot);
|
|
182
|
+
let current = root.resolvedRoot;
|
|
183
|
+
const parentComponents = path.dirname(MATERIAL_REGISTRY_PATH).split("/");
|
|
184
|
+
for (const component of parentComponents) {
|
|
185
|
+
current = path.join(current, component);
|
|
186
|
+
try { fileSystem.mkdirSync(current, { mode: 0o755 }); } catch (error) { if (error.code !== "EEXIST") fail("material_registry_output_parent_invalid", `output parent cannot be created: ${error.message}`, MATERIAL_REGISTRY_PATH); }
|
|
187
|
+
let stats;
|
|
188
|
+
try { stats = fileSystem.lstatSync(current); } catch (error) { fail("material_registry_output_parent_invalid", `output parent is unavailable: ${error.message}`, MATERIAL_REGISTRY_PATH); }
|
|
189
|
+
if (stats.isSymbolicLink()) fail("material_registry_output_parent_symlink", "output parent components must not be symlinks", MATERIAL_REGISTRY_PATH);
|
|
190
|
+
if (!stats.isDirectory()) fail("material_registry_output_parent_invalid", "output parent components must be directories", MATERIAL_REGISTRY_PATH);
|
|
191
|
+
const canonical = realpath(fileSystem, current);
|
|
192
|
+
const expected = path.resolve(root.canonicalRoot, ...path.relative(root.resolvedRoot, current).split(path.sep));
|
|
193
|
+
if (canonical !== expected || (canonical !== root.canonicalRoot && !canonical.startsWith(`${root.canonicalRoot}${path.sep}`))) {
|
|
194
|
+
fail("material_registry_output_parent_escape", "output parent must remain inside the repository", MATERIAL_REGISTRY_PATH);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
const stats = fileSystem.lstatSync(current);
|
|
198
|
+
return { path: current, canonical: realpath(fileSystem, current), dev: stats.dev, ino: stats.ino };
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function parentUnchanged(parent, fileSystem) {
|
|
202
|
+
try {
|
|
203
|
+
const stats = fileSystem.lstatSync(parent.path);
|
|
204
|
+
return stats.isDirectory() && !stats.isSymbolicLink() && stats.dev === parent.dev && stats.ino === parent.ino && realpath(fileSystem, parent.path) === parent.canonical;
|
|
205
|
+
} catch { return false; }
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
function inspectOutputTarget(target, fileSystem) {
|
|
209
|
+
let stats;
|
|
210
|
+
try { stats = fileSystem.lstatSync(target); } catch (error) {
|
|
211
|
+
if (error.code === "ENOENT") return { exists: false };
|
|
212
|
+
fail("material_registry_output_invalid", `output target is unavailable: ${error.message}`, MATERIAL_REGISTRY_PATH);
|
|
213
|
+
}
|
|
214
|
+
if (stats.isSymbolicLink()) fail("material_registry_output_symlink", "output target must not be a symlink", MATERIAL_REGISTRY_PATH);
|
|
215
|
+
if (!stats.isFile()) fail("material_registry_output_type_invalid", "output target must be a regular file", MATERIAL_REGISTRY_PATH);
|
|
216
|
+
return { exists: true, dev: stats.dev, ino: stats.ino };
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
function readOutputTarget(target, expected, fileSystem) {
|
|
220
|
+
let descriptor;
|
|
221
|
+
try {
|
|
222
|
+
descriptor = fileSystem.openSync(target, fs.constants.O_RDONLY | (fs.constants.O_NOFOLLOW ?? 0));
|
|
223
|
+
const stats = fileSystem.fstatSync(descriptor);
|
|
224
|
+
const current = fileSystem.lstatSync(target);
|
|
225
|
+
if (!stats.isFile() || current.isSymbolicLink() || !current.isFile()
|
|
226
|
+
|| (expected?.exists && (stats.dev !== expected.dev || stats.ino !== expected.ino || current.dev !== expected.dev || current.ino !== expected.ino))) {
|
|
227
|
+
fail("material_registry_output_race", "output target identity changed during read", MATERIAL_REGISTRY_PATH);
|
|
228
|
+
}
|
|
229
|
+
return fileSystem.readFileSync(descriptor);
|
|
230
|
+
} catch (error) {
|
|
231
|
+
if (error instanceof MaterialRegistryError) throw error;
|
|
232
|
+
const code = error.code === "ELOOP" ? "material_registry_output_symlink" : "material_registry_output_read_failed";
|
|
233
|
+
fail(code, `output target cannot be read safely: ${error.message}`, MATERIAL_REGISTRY_PATH);
|
|
234
|
+
} finally { if (descriptor !== undefined) fileSystem.closeSync(descriptor); }
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
function postWriteValidation(repositoryRoot, manifest, expectedBytes, target, parent, fileSystem) {
|
|
238
|
+
if (!parentUnchanged(parent, fileSystem)) fail("material_registry_output_parent_race", "output parent identity changed during publication", MATERIAL_REGISTRY_PATH);
|
|
239
|
+
const targetState = inspectOutputTarget(target, fileSystem);
|
|
240
|
+
if (!targetState.exists) fail("material_registry_postwrite_invalid", "published registry is missing", MATERIAL_REGISTRY_PATH);
|
|
241
|
+
const finalBytes = readOutputTarget(target, targetState, fileSystem);
|
|
242
|
+
if (!finalBytes.equals(expectedBytes)) fail("material_registry_postwrite_invalid", "published registry bytes differ from generated canonical bytes", MATERIAL_REGISTRY_PATH);
|
|
243
|
+
let persisted;
|
|
244
|
+
try { persisted = JSON.parse(finalBytes.toString("utf8")); } catch (error) { fail("material_registry_postwrite_invalid", `published registry JSON is invalid: ${error.message}`, MATERIAL_REGISTRY_PATH); }
|
|
245
|
+
if (canonicalize(persisted) !== finalBytes.toString("utf8") || canonicalize(persisted) !== canonicalize(manifest)) {
|
|
246
|
+
fail("material_registry_postwrite_invalid", "published registry is not the generated canonical manifest", MATERIAL_REGISTRY_PATH);
|
|
247
|
+
}
|
|
248
|
+
const validation = validateMaterialRegistry({ repositoryRoot, manifest: persisted });
|
|
249
|
+
if (!validation.ok) {
|
|
250
|
+
const first = validation.failures[0];
|
|
251
|
+
const trackingCodes = new Set(["material_path_untracked", "material_git_mode_invalid", "material_inventory_failed"]);
|
|
252
|
+
const code = trackingCodes.has(first.code) ? "material_registry_postwrite_tracking_drift" : "material_registry_postwrite_source_drift";
|
|
253
|
+
fail(code, `post-write validation failed: ${first.code}`, first.path);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
export function writeMaterialRegistry(options) {
|
|
258
|
+
assertOptions(options, new Set(["repositoryRoot", "fileSystem"]));
|
|
259
|
+
const fileSystem = options.fileSystem ?? fs;
|
|
260
|
+
if (!fileSystem || typeof fileSystem !== "object") fail("material_registry_option_invalid", "fileSystem must implement the filesystem contract");
|
|
261
|
+
const manifest = generateMaterialRegistry({ repositoryRoot: options.repositoryRoot });
|
|
262
|
+
const bytes = Buffer.from(canonicalize(manifest), "utf8");
|
|
263
|
+
const parent = inspectOutputParent(options.repositoryRoot, fileSystem);
|
|
264
|
+
const target = path.join(parent.path, path.basename(MATERIAL_REGISTRY_PATH));
|
|
265
|
+
const initial = inspectOutputTarget(target, fileSystem);
|
|
266
|
+
let changed = true;
|
|
267
|
+
if (initial.exists) changed = !readOutputTarget(target, initial, fileSystem).equals(bytes);
|
|
268
|
+
let temporary;
|
|
269
|
+
let descriptor;
|
|
270
|
+
try {
|
|
271
|
+
if (changed) {
|
|
272
|
+
temporary = path.join(parent.path, `.material-registry.json.${process.pid}.${randomBytes(12).toString("hex")}`);
|
|
273
|
+
try {
|
|
274
|
+
descriptor = fileSystem.openSync(temporary, fs.constants.O_WRONLY | fs.constants.O_CREAT | fs.constants.O_EXCL | (fs.constants.O_NOFOLLOW ?? 0), 0o600);
|
|
275
|
+
fileSystem.writeFileSync(descriptor, bytes);
|
|
276
|
+
fileSystem.fsyncSync(descriptor);
|
|
277
|
+
fileSystem.fchmodSync(descriptor, 0o644);
|
|
278
|
+
} catch (error) {
|
|
279
|
+
fail("material_registry_output_write_failed", `temporary registry write failed: ${error.message}`, MATERIAL_REGISTRY_PATH);
|
|
280
|
+
} finally {
|
|
281
|
+
if (descriptor !== undefined) { fileSystem.closeSync(descriptor); descriptor = undefined; }
|
|
282
|
+
}
|
|
283
|
+
if (!parentUnchanged(parent, fileSystem)) fail("material_registry_output_parent_race", "output parent changed before publication", MATERIAL_REGISTRY_PATH);
|
|
284
|
+
const current = inspectOutputTarget(target, fileSystem);
|
|
285
|
+
if (initial.exists !== current.exists || (initial.exists && (initial.dev !== current.dev || initial.ino !== current.ino))) {
|
|
286
|
+
if (current.exists && readOutputTarget(target, current, fileSystem).equals(bytes)) changed = false;
|
|
287
|
+
else fail("material_registry_output_race", "output target changed before publication", MATERIAL_REGISTRY_PATH);
|
|
288
|
+
}
|
|
289
|
+
if (changed) {
|
|
290
|
+
try { fileSystem.renameSync(temporary, target); temporary = undefined; }
|
|
291
|
+
catch (error) {
|
|
292
|
+
const code = parentUnchanged(parent, fileSystem) ? "material_registry_output_publish_failed" : "material_registry_output_parent_race";
|
|
293
|
+
fail(code, `atomic registry publication failed: ${error.message}`, MATERIAL_REGISTRY_PATH);
|
|
294
|
+
}
|
|
295
|
+
let parentDescriptor;
|
|
296
|
+
try { parentDescriptor = fileSystem.openSync(parent.path, fs.constants.O_RDONLY); fileSystem.fsyncSync(parentDescriptor); }
|
|
297
|
+
catch (error) { fail("material_registry_output_sync_failed", `output directory sync failed: ${error.message}`, MATERIAL_REGISTRY_PATH); }
|
|
298
|
+
finally { if (parentDescriptor !== undefined) fileSystem.closeSync(parentDescriptor); }
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
postWriteValidation(options.repositoryRoot, manifest, bytes, target, parent, fileSystem);
|
|
302
|
+
} finally {
|
|
303
|
+
if (descriptor !== undefined) fileSystem.closeSync(descriptor);
|
|
304
|
+
if (temporary !== undefined) { try { fileSystem.unlinkSync(temporary); } catch (error) { if (error.code !== "ENOENT") throw error; } }
|
|
305
|
+
}
|
|
306
|
+
return deepFreeze({ changed, byte_length: bytes.byteLength, path: MATERIAL_REGISTRY_PATH, sha256: sha256(bytes), version_id: manifest.version_id });
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
export function readManifestBoundMaterial(options) {
|
|
310
|
+
assertOptions(options, new Set(["repositoryRoot", "manifest", "reference"]));
|
|
311
|
+
const validation = validateMaterialRegistry({ repositoryRoot: options.repositoryRoot, manifest: options.manifest });
|
|
312
|
+
if (!validation.ok) fail(validation.failures[0].code, validation.failures[0].message, validation.failures[0].path);
|
|
313
|
+
const record = resolveMaterialRecord({ manifest: options.manifest, reference: options.reference });
|
|
314
|
+
const inspection = inspectPath(repositoryState(options.repositoryRoot), record.repository_path);
|
|
315
|
+
const bytes = readInspected(inspection, record.repository_path);
|
|
316
|
+
if (sha256(bytes) !== record.source_sha256) fail("material_source_hash_mismatch", "material bytes do not match source_sha256", record.repository_path);
|
|
317
|
+
if (bytes.byteLength !== record.byte_length) fail("material_byte_length_mismatch", "material bytes do not match byte_length", record.repository_path);
|
|
318
|
+
return { record, bytes };
|
|
319
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { realpathSync } from "node:fs";
|
|
4
|
+
import { Transform } from "node:stream";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
|
|
7
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
8
|
+
|
|
9
|
+
import { createMaterialMcpServer } from "./agent-native/v2/material-mcp-adapter.mjs";
|
|
10
|
+
|
|
11
|
+
// Maximum raw bytes in one newline-delimited JSON-RPC payload, excluding the LF delimiter.
|
|
12
|
+
export const MATERIAL_MCP_MAX_FRAME_BYTES = 1024 * 1024;
|
|
13
|
+
export const MATERIAL_MCP_FRAME_LIMIT_MESSAGE = `material MCP input frame exceeds ${MATERIAL_MCP_MAX_FRAME_BYTES}-byte limit`;
|
|
14
|
+
|
|
15
|
+
export class MaterialMcpStdin extends Transform {
|
|
16
|
+
constructor(maxFrameBytes = MATERIAL_MCP_MAX_FRAME_BYTES) {
|
|
17
|
+
super();
|
|
18
|
+
this.maxFrameBytes = maxFrameBytes;
|
|
19
|
+
this.frame = Buffer.allocUnsafe(maxFrameBytes + 1);
|
|
20
|
+
this.frameBytes = 0;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
_transform(chunk, encoding, callback) {
|
|
24
|
+
const bytes = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk, encoding);
|
|
25
|
+
let offset = 0;
|
|
26
|
+
while (offset < bytes.byteLength) {
|
|
27
|
+
const newline = bytes.indexOf(0x0a, offset);
|
|
28
|
+
const end = newline === -1 ? bytes.byteLength : newline;
|
|
29
|
+
const segmentBytes = end - offset;
|
|
30
|
+
if (this.frameBytes + segmentBytes > this.maxFrameBytes) {
|
|
31
|
+
this.frameBytes = 0;
|
|
32
|
+
const error = new Error(MATERIAL_MCP_FRAME_LIMIT_MESSAGE);
|
|
33
|
+
error.code = "ERR_MATERIAL_MCP_FRAME_TOO_LARGE";
|
|
34
|
+
callback(error);
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
if (segmentBytes > 0) {
|
|
38
|
+
bytes.copy(this.frame, this.frameBytes, offset, end);
|
|
39
|
+
this.frameBytes += segmentBytes;
|
|
40
|
+
}
|
|
41
|
+
if (newline === -1) break;
|
|
42
|
+
this.frame[this.frameBytes] = 0x0a;
|
|
43
|
+
this.push(Buffer.from(this.frame.subarray(0, this.frameBytes + 1)));
|
|
44
|
+
this.frameBytes = 0;
|
|
45
|
+
offset = newline + 1;
|
|
46
|
+
}
|
|
47
|
+
callback();
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
_destroy(error, callback) {
|
|
51
|
+
this.frame = Buffer.alloc(0);
|
|
52
|
+
this.frameBytes = 0;
|
|
53
|
+
callback(error);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
async function main() {
|
|
58
|
+
const server = createMaterialMcpServer();
|
|
59
|
+
const input = new MaterialMcpStdin();
|
|
60
|
+
const transport = new StdioServerTransport(input);
|
|
61
|
+
let failed = false;
|
|
62
|
+
const failOversizedFrame = (error) => {
|
|
63
|
+
if (failed || error?.code !== "ERR_MATERIAL_MCP_FRAME_TOO_LARGE") return;
|
|
64
|
+
failed = true;
|
|
65
|
+
process.stderr.write(`${error.message}\n`);
|
|
66
|
+
process.exitCode = 1;
|
|
67
|
+
process.stdin.destroy();
|
|
68
|
+
void transport.close();
|
|
69
|
+
};
|
|
70
|
+
input.on("error", failOversizedFrame);
|
|
71
|
+
transport.onerror = failOversizedFrame;
|
|
72
|
+
process.stdout.on("error", (error) => {
|
|
73
|
+
if (error?.code !== "EPIPE") throw error;
|
|
74
|
+
process.stderr.write("material MCP output closed\n");
|
|
75
|
+
process.exitCode = 1;
|
|
76
|
+
process.stdin.destroy();
|
|
77
|
+
void transport.close();
|
|
78
|
+
});
|
|
79
|
+
await server.connect(transport);
|
|
80
|
+
process.stdin.pipe(input);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function isDirectRun(entryPath) {
|
|
84
|
+
if (!entryPath) return false;
|
|
85
|
+
try {
|
|
86
|
+
return realpathSync(entryPath) === realpathSync(fileURLToPath(import.meta.url));
|
|
87
|
+
} catch (error) {
|
|
88
|
+
if (["ENOENT", "ENOTDIR"].includes(error?.code)) return false;
|
|
89
|
+
throw error;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (isDirectRun(process.argv[1])) {
|
|
94
|
+
await main().catch((error) => {
|
|
95
|
+
process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`);
|
|
96
|
+
process.exitCode = 1;
|
|
97
|
+
});
|
|
98
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
function emergencyReport() {
|
|
4
|
+
return {
|
|
5
|
+
failures: [{ code: "material_cli_failed", message: "material CLI failed" }],
|
|
6
|
+
ok: false,
|
|
7
|
+
operation: null,
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function writeProtocol(output, exitCode) {
|
|
12
|
+
process.stdout.on("error", () => { process.exitCode = 1; });
|
|
13
|
+
process.stdout.write(output);
|
|
14
|
+
process.exitCode = exitCode;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
async function main() {
|
|
18
|
+
try {
|
|
19
|
+
const { runMaterialCli } = await import("./agent-native/v2/material-cli-adapter.mjs");
|
|
20
|
+
const result = runMaterialCli(process.argv.slice(2));
|
|
21
|
+
writeProtocol(result.output, result.exitCode);
|
|
22
|
+
} catch {
|
|
23
|
+
writeProtocol(`${JSON.stringify(emergencyReport(), null, 2)}\n`, 1);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
await main();
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
import path from "node:path";
|
|
2
|
-
import StyleDictionary from "style-dictionary";
|
|
3
|
-
|
|
4
|
-
export const adapter = Object.freeze({ name: "style-dictionary", version: "5.5.0" });
|
|
5
|
-
|
|
6
|
-
const durationTransform = "stylegallery/duration-css";
|
|
7
|
-
if (!Object.hasOwn(StyleDictionary.hooks.transforms, durationTransform)) {
|
|
8
|
-
StyleDictionary.registerTransform({
|
|
9
|
-
filter: (token) => token.$type === "duration",
|
|
10
|
-
name: durationTransform,
|
|
11
|
-
transform: (token) => `${token.$value.value}${token.$value.unit}`,
|
|
12
|
-
transitive: true,
|
|
13
|
-
type: "value",
|
|
14
|
-
});
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export function createStyleDictionaryConfig(source, output) {
|
|
18
|
-
return {
|
|
19
|
-
log: { verbosity: "verbose", warnings: "warn" },
|
|
20
|
-
platforms: {
|
|
21
|
-
css: {
|
|
22
|
-
buildPath: `${path.dirname(output)}${path.sep}`,
|
|
23
|
-
files: [
|
|
24
|
-
{
|
|
25
|
-
destination: path.basename(output),
|
|
26
|
-
format: "css/variables",
|
|
27
|
-
options: { outputReferences: true },
|
|
28
|
-
},
|
|
29
|
-
],
|
|
30
|
-
transforms: [...StyleDictionary.hooks.transformGroups.css, durationTransform],
|
|
31
|
-
},
|
|
32
|
-
},
|
|
33
|
-
source: [source],
|
|
34
|
-
usesDtcg: true,
|
|
35
|
-
};
|
|
36
|
-
}
|