mirror of
https://github.com/clockworklabs/SpacetimeDB.git
synced 2026-06-27 08:18:48 -04:00
dc20faa611
## Summary - Fix TypeScript view examples to use `ctx.sender` as a property, matching the server SDK `ViewCtx` API. - Update the architecture overview TypeScript view snippet to use `players.rowType` and `undefined` for optional view returns. - Clarify shared `ViewContext` prose so it does not imply every language uses a callable `ctx.sender()` API. - Improve docs agent-readiness metadata: - publish `/docs/robots.txt` with a docs sitemap directive and Content-Signal policy - add Markdown alternate links for the existing `/docs/llms.txt` and `/docs/llms-full.txt` outputs - generate `/.well-known/agent-skills`-style discovery metadata under `/docs/.well-known/agent-skills/` from the repo's existing `skills/` source files during docs builds ## Validation - `pnpm --dir docs build` - `pnpm --dir docs typecheck` - Verified the docs build emits `robots.txt` and `.well-known/agent-skills/index.json`. - Verified generated Agent Skills SHA-256 digests match the emitted `SKILL.md` artifacts. ## Notes The Cloudflare agent-readiness scan for `spacetimedb.com` still depends on the root/marketing host exposing root-level files such as `/robots.txt`, `/sitemap.xml`, and `/.well-known/agent-skills/index.json`. This PR makes the docs origin produce the corresponding docs-scoped artifacts at `/docs/...`; the root host can route or mirror these if we want the exact `spacetimedb.com` scan to pick them up. --------- Co-authored-by: clockwork-labs-bot <clockwork-labs-bot@users.noreply.github.com> Co-authored-by: rain <rain@rain.local> Co-authored-by: Tyler Cloutier <cloutiertyler@users.noreply.github.com>
74 lines
2.1 KiB
JavaScript
74 lines
2.1 KiB
JavaScript
import { createHash } from 'node:crypto';
|
|
import { mkdir, readdir, readFile, rm, writeFile } from 'node:fs/promises';
|
|
import path from 'node:path';
|
|
import { fileURLToPath } from 'node:url';
|
|
|
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
const repoRoot = path.resolve(__dirname, '../..');
|
|
const sourceDir = path.join(repoRoot, 'skills');
|
|
const outputDir = path.join(
|
|
repoRoot,
|
|
'docs/static/.well-known/agent-skills'
|
|
);
|
|
function readFrontmatter(markdown, sourcePath) {
|
|
const match = markdown.match(/^---\n([\s\S]*?)\n---\n/);
|
|
if (!match) {
|
|
throw new Error(`${sourcePath} is missing YAML frontmatter`);
|
|
}
|
|
|
|
const frontmatter = {};
|
|
for (const line of match[1].split('\n')) {
|
|
const field = line.match(/^([a-zA-Z0-9_-]+):\s*(.*)$/);
|
|
if (field) {
|
|
frontmatter[field[1]] = field[2].replace(/^"(.*)"$/, '$1');
|
|
}
|
|
}
|
|
|
|
if (!frontmatter.name || !frontmatter.description) {
|
|
throw new Error(`${sourcePath} must declare name and description`);
|
|
}
|
|
|
|
return frontmatter;
|
|
}
|
|
|
|
await rm(outputDir, { recursive: true, force: true });
|
|
await mkdir(outputDir, { recursive: true });
|
|
|
|
const skills = [];
|
|
|
|
for (const entry of (await readdir(sourceDir, { withFileTypes: true })).sort(
|
|
(a, b) => a.name.localeCompare(b.name)
|
|
)) {
|
|
if (!entry.isDirectory()) {
|
|
continue;
|
|
}
|
|
|
|
const skillPath = path.join(sourceDir, entry.name, 'SKILL.md');
|
|
const markdown = await readFile(skillPath, 'utf8');
|
|
const metadata = readFrontmatter(markdown, skillPath);
|
|
|
|
const skillOutputDir = path.join(outputDir, metadata.name);
|
|
await mkdir(skillOutputDir, { recursive: true });
|
|
await writeFile(path.join(skillOutputDir, 'SKILL.md'), markdown);
|
|
|
|
skills.push({
|
|
name: metadata.name,
|
|
type: 'skill-md',
|
|
description: metadata.description,
|
|
url: `/docs/.well-known/agent-skills/${metadata.name}/SKILL.md`,
|
|
digest: `sha256:${createHash('sha256').update(markdown).digest('hex')}`,
|
|
});
|
|
}
|
|
|
|
await writeFile(
|
|
path.join(outputDir, 'index.json'),
|
|
`${JSON.stringify(
|
|
{
|
|
$schema: 'https://schemas.agentskills.io/discovery/0.2.0/schema.json',
|
|
skills,
|
|
},
|
|
null,
|
|
2
|
|
)}\n`
|
|
);
|