Dependency audit / audit (push) Canceled after 0s
CI / plan (push) Canceled after 0s
Gitoxide helper admission / macos-latest (push) Canceled after 0s
Gitoxide helper admission / ubuntu-latest (push) Canceled after 0s
Gitoxide helper admission / windows-latest (push) Canceled after 0s
Runtime Host peer admission / quality (push) Canceled after 0s
Windows recovery / windows_recovery (push) Canceled after 0s
CI / heavy (push) Canceled after 0s
CI / test (push) Canceled after 0s
121 lines
4.2 KiB
JavaScript
121 lines
4.2 KiB
JavaScript
#!/usr/bin/env node
|
|
/*
|
|
* Licensed to the Apache Software Foundation (ASF) under one
|
|
* or more contributor license agreements. See the NOTICE file
|
|
* distributed with this work for additional information
|
|
* regarding copyright ownership. The ASF licenses this file
|
|
* to you under the Apache License, Version 2.0 (the
|
|
* "License"); you may not use this file except in compliance
|
|
* with the License. You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing,
|
|
* software distributed under the License is distributed on an
|
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
* KIND, either express or implied. See the License for the
|
|
* specific language governing permissions and limitations
|
|
* under the License.
|
|
*/
|
|
|
|
import { createHash } from 'node:crypto';
|
|
import { readdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
import { dirname, join } from 'node:path';
|
|
import { fileURLToPath } from 'node:url';
|
|
|
|
const repositoryRoot = dirname(dirname(fileURLToPath(import.meta.url)));
|
|
const sourcesDir = join(repositoryRoot, 'packages', 'runtime', 'resources', 'bundled-skills');
|
|
const outFile = join(
|
|
repositoryRoot,
|
|
'packages',
|
|
'runtime',
|
|
'src',
|
|
'bundled-skill-catalog.generated.ts',
|
|
);
|
|
|
|
const ID_RE = /^[A-Za-z0-9][A-Za-z0-9._-]{0,80}$/;
|
|
const LEGACY_CONTENT_SHA256_BY_ID = {
|
|
'computer-use': [
|
|
'sha256:419088b2f8a0b12061b4811323abc381869ebe8fccbfc8f2bdfc96ff37a1e45b',
|
|
'sha256:8e4404349be4e5493fcf13981624ed55198c0670a794fbf88e2bad81ddb79f6c',
|
|
],
|
|
};
|
|
|
|
export function readBundledSkillSources(dir = sourcesDir) {
|
|
const ids = readdirSync(dir, { withFileTypes: true })
|
|
.filter((entry) => entry.isDirectory() && ID_RE.test(entry.name))
|
|
.map((entry) => entry.name)
|
|
.sort((a, b) => a.localeCompare(b));
|
|
|
|
const sourceIds = new Set(ids);
|
|
const orphanedLegacyIds = Object.keys(LEGACY_CONTENT_SHA256_BY_ID).filter(
|
|
(id) => !sourceIds.has(id),
|
|
);
|
|
if (orphanedLegacyIds.length > 0) {
|
|
throw new Error(
|
|
`legacy content hashes reference missing bundled skills: ${orphanedLegacyIds.join(', ')}`,
|
|
);
|
|
}
|
|
|
|
return ids.map((id) => {
|
|
const body = readFileSync(join(dir, id, 'SKILL.md'), 'utf8');
|
|
if (!body.startsWith('---')) {
|
|
throw new Error(`bundled skill ${id} is missing YAML front-matter`);
|
|
}
|
|
return {
|
|
id,
|
|
body,
|
|
sourceName: 'maka-bundled',
|
|
sourceVersion: '1',
|
|
contentSha256: `sha256:${createHash('sha256').update(body).digest('hex')}`,
|
|
legacyContentSha256: LEGACY_CONTENT_SHA256_BY_ID[id] ?? [],
|
|
};
|
|
});
|
|
}
|
|
|
|
export function renderGeneratedModule(skills) {
|
|
const entries = skills
|
|
.map(
|
|
(skill) =>
|
|
` { id: ${JSON.stringify(skill.id)}, body: ${JSON.stringify(skill.body)}, sourceName: ${JSON.stringify(skill.sourceName)}, sourceVersion: ${JSON.stringify(skill.sourceVersion)}, contentSha256: ${JSON.stringify(skill.contentSha256)}, legacyContentSha256: ${JSON.stringify(skill.legacyContentSha256)} },`,
|
|
)
|
|
.join('\n');
|
|
return `// @generated by scripts/gen-bundled-skill-catalog.mjs — do not edit by hand.
|
|
// Source of truth: packages/runtime/resources/bundled-skills/<id>/SKILL.md
|
|
// Regenerate: npm run generate:bundled-skills
|
|
|
|
export interface BundledSkillSource {
|
|
id: string;
|
|
body: string;
|
|
sourceName: string;
|
|
sourceVersion: string;
|
|
contentSha256: string;
|
|
legacyContentSha256: readonly string[];
|
|
}
|
|
|
|
// biome-ignore format: generated catalog keeps one reviewable source per line.
|
|
export const BUNDLED_SKILL_CATALOG: ReadonlyArray<BundledSkillSource> = [
|
|
${entries}
|
|
];
|
|
`;
|
|
}
|
|
|
|
export function expectedBundledSkillCatalog() {
|
|
return renderGeneratedModule(readBundledSkillSources());
|
|
}
|
|
|
|
function main() {
|
|
const expected = expectedBundledSkillCatalog();
|
|
if (process.argv.includes('--check')) {
|
|
if (readFileSync(outFile, 'utf8') !== expected) {
|
|
throw new Error('Bundled Skill catalog is stale; run npm run generate:bundled-skills');
|
|
}
|
|
console.log('[gen] bundled Skill catalog is current');
|
|
return;
|
|
}
|
|
writeFileSync(outFile, expected, 'utf8');
|
|
console.log(`[gen] wrote ${readBundledSkillSources().length} bundled skills -> ${outFile}`);
|
|
}
|
|
|
|
if (process.argv[1] && fileURLToPath(import.meta.url) === process.argv[1]) main();
|