Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 25 additions & 5 deletions scripts/fetch-skill-registry.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import { mkdir, readFile, writeFile } from 'node:fs/promises';
// then: every dependency names another skill in the same registry
// class: correctness
// === END CONTRACTS ===
// Usage: run `npm run refresh:skills` after `npm run refresh:github`; WebMCP consumes `/assets/data/skill-registry.json`. Use `OFFLINE=1` only after at least one verified online refresh has created the last-known-good snapshot.
// Usage: run `npm run refresh:skills` after `npm run refresh:github`; WebMCP consumes `/assets/data/skill-registry.json`. `OFFLINE=1 npm run refresh:skills` is valid on a clean checkout because the committed bootstrap snapshot is pinned to an exact skill-lib commit and Git blob; successful online refreshes replace it with the newest verified normalized snapshot.

const ORGANIZATION = 'The-Interdependency';
const REPOSITORY = 'skill-lib';
Expand All @@ -36,11 +36,21 @@ const GENERATED_REPOS = 'src/_data/generated/repos.json';
const GENERATED_OUT = 'src/_data/generated/skillRegistry.json';
const PUBLIC_OUT = 'src/assets/data/skill-registry.json';
const SNAPSHOT_OUT = 'src/_data/snapshots/skill-registry.last-known-good.json';
const BOOTSTRAP_SNAPSHOT_COMMIT = '260671303733a45c8f8d5563e41d8854e09856e6';
const BOOTSTRAP_SNAPSHOT_BLOB = '7f71adeadac07a751b953c39e38dd78be599976f';

function sha256(value) {
return createHash('sha256').update(value).digest('hex');
}

function gitBlobSha1(value) {
const body = Buffer.from(value);
return createHash('sha1')
.update(`blob ${body.length}\0`)
.update(body)
.digest('hex');
}

function stableJson(value) {
return `${JSON.stringify(value, null, 2)}\n`;
}
Expand Down Expand Up @@ -102,13 +112,23 @@ export function normalizeRegistry(sourceText, commit) {
};
}

async function readFallback() {
export async function readFallback() {
const text = await readFile(SNAPSHOT_OUT, 'utf8');
const parsed = JSON.parse(text);
if (!parsed?.source?.commit || !Array.isArray(parsed.skills) || parsed.skills.length === 0) {
throw new Error('last-known-good skill registry snapshot is invalid');

if (parsed?.source?.commit && Array.isArray(parsed.skills) && parsed.skills.length > 0) {
return parsed;
}
return parsed;

if (parsed?.repo === `${ORGANIZATION}/${REPOSITORY}` && Array.isArray(parsed.skills) && parsed.skills.length > 0) {
const actualBlob = gitBlobSha1(text);
if (actualBlob !== BOOTSTRAP_SNAPSHOT_BLOB) {
Comment on lines +124 to +125

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve snapshot bytes across Git line-ending conversion

On a clean checkout with core.autocrlf=true, such as a typical Windows checkout, Git can rewrite this JSON snapshot from LF to CRLF because the repository has no .gitattributes rule fixing its line endings. gitBlobSha1(text) then hashes the converted working-tree bytes, so this comparison rejects the otherwise exact committed bootstrap and OFFLINE=1 fails in the clean-checkout scenario this change promises to support. Enforce LF or binary treatment for the snapshot, or verify canonicalized bytes.

Useful? React with 👍 / 👎.

throw new Error(`bootstrap skill registry snapshot blob mismatch: ${actualBlob}`);
}
return normalizeRegistry(text, BOOTSTRAP_SNAPSHOT_COMMIT);
}

throw new Error('last-known-good skill registry snapshot is invalid');
}

async function writeProjection(registry, fallback, hmmm = []) {
Expand Down
Loading
Loading