Files
playbook/antigravity-awesome-skills/tools/scripts/build-aas-v1-offline-catalog.js
T
2026-07-20 00:03:02 +00:00

95 lines
3.7 KiB
JavaScript

#!/usr/bin/env node
"use strict";
const fs = require("node:fs");
const path = require("node:path");
const packageMetadata = require("../../package.json");
const { canonicalJson, sha256 } = require("../lib/aas-v1/canonical-json");
const versions = require("../lib/aas-v1/versions");
const ROOT = path.resolve(__dirname, "../..");
const OUTPUT_ROOT = path.join(ROOT, "data", "aas-v1");
const CONTENT_RELATIVE = "data/aas-v1/skill-content.v1.ndjson";
const INDEX_RELATIVE = "data/aas-v1/skill-content-index.v1.json";
const MANIFEST_RELATIVE = "data/aas-v1/catalog-manifest.v1.json";
const STATIC_ASSETS = [
"data/catalog.json",
"tools/lib/aas-v1/ontology.v1.json",
];
function compareStrings(left, right) {
return left < right ? -1 : (left > right ? 1 : 0);
}
function record(relativePath, bytes) {
return { path: relativePath, size: bytes.length, sha256: sha256(bytes) };
}
function buildArtifacts() {
const index = JSON.parse(fs.readFileSync(path.join(ROOT, "skills_index.json"), "utf8"));
const entries = [...index].sort((left, right) => compareStrings(left.id, right.id));
const seen = new Set();
const lines = [];
const contentIndex = { schemaVersion: 1, entries: {} };
let offset = 0;
for (const entry of entries) {
if (seen.has(entry.id)) throw new Error(`Duplicate canonical skill ID: ${entry.id}`);
seen.add(entry.id);
const relativeSkillPath = `${entry.path}/SKILL.md`;
const skillPath = path.resolve(ROOT, ...relativeSkillPath.split("/"));
if (skillPath !== ROOT && !skillPath.startsWith(`${ROOT}${path.sep}`)) throw new Error(`Skill path escaped root: ${entry.id}`);
const text = fs.readFileSync(skillPath, "utf8");
const line = Buffer.from(`${canonicalJson({ id: entry.id, text, sha256: sha256(Buffer.from(text)) })}\n`);
lines.push(line);
contentIndex.entries[entry.id] = {
offset,
length: line.length,
sha256: sha256(line),
};
offset += line.length;
}
const contentBytes = Buffer.concat(lines);
const indexBytes = Buffer.from(`${canonicalJson(contentIndex)}\n`);
const generated = new Map([
[CONTENT_RELATIVE, contentBytes],
[INDEX_RELATIVE, indexBytes],
]);
const assets = [
...STATIC_ASSETS.map((relativePath) => record(relativePath, fs.readFileSync(path.join(ROOT, ...relativePath.split("/"))))),
...[...generated].map(([relativePath, bytes]) => record(relativePath, bytes)),
].sort((left, right) => compareStrings(left.path, right.path));
const catalogDigest = sha256(canonicalJson({ digestVersion: 1, assets }));
const manifest = {
schemaVersion: 1,
package: packageMetadata.name,
packageVersion: packageMetadata.version,
catalogSchemaVersion: versions.catalogSchemaVersion,
digestVersion: 1,
catalogDigest,
assets,
skillCount: entries.length,
};
generated.set(MANIFEST_RELATIVE, Buffer.from(`${canonicalJson(manifest)}\n`));
return { catalogDigest, generated, manifest };
}
function main() {
const check = process.argv.includes("--check");
const { generated, manifest } = buildArtifacts();
const stale = [];
for (const [relativePath, bytes] of generated) {
const outputPath = path.join(ROOT, ...relativePath.split("/"));
if (!fs.existsSync(outputPath) || !fs.readFileSync(outputPath).equals(bytes)) stale.push(relativePath);
if (!check) {
fs.mkdirSync(path.dirname(outputPath), { recursive: true });
fs.writeFileSync(outputPath, bytes, { mode: 0o644 });
}
}
if (check && stale.length) throw new Error(`Offline catalog assets are stale: ${stale.join(", ")}`);
process.stdout.write(`${check ? "Validated" : "Wrote"} ${manifest.skillCount} skill records; catalog ${manifest.catalogDigest}.\n`);
}
if (require.main === module) main();
module.exports = { buildArtifacts };