openvibers/openvibe.space Commits

openvibe-shared v4.108.0 (was v4.107.4) (#198)

OP OpenVibers committed 10 days ago · 2eef8bc164 · 9 files, +42 −16

* openvibe-shared v4.108.0 (was v4.107.4)

Opened by scripts/pin-bump.js in OpenVibers/OpenVibe.Shared after the v4.108.0 release.

* llms.txt links each page at its Markdown twin, with an ## Optional section (AGT-5)

Changes

ddiff --git a/README.md b/README.md
iindex 75459ee..6cb02fb 100644
---- a/README.md
++++ b/README.md
@@@ -151,5 +151,5 @@ AGPL-3.0-only. See [LICENSE](LICENSE).
<!-- versions:start -->
- openvibe-contracts: v0.266.0
- openvibe-sdk: v0.82.0
--- openvibe-shared: v4.107.4
++- openvibe-shared: v4.108.0
<!-- versions:end -->
ddiff --git a/STATUS.json b/STATUS.json
iindex 1afa108..c9c7e2e 100644
---- a/STATUS.json
++++ b/STATUS.json
@@@ -8,7 +8,7 @@
"domain": "openvibe.space",
"contracts": "openvibe-contracts v0.266.0",
"sdk": "openvibe-sdk v0.82.0",
-- "shared": "openvibe-shared v4.107.4",
++ "shared": "openvibe-shared v4.108.0",
"capabilities": [
"space.site.manage",
"space.site.config",
ddiff --git a/package-lock.json b/package-lock.json
iindex faaa71b..a35fe60 100644
---- a/package-lock.json
++++ b/package-lock.json
@@@ -17,7 +17,7 @@
"helmet": "^8.1.0",
"openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.266.0",
"openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.82.0",
-- "openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v4.107.4",
++ "openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v4.108.0",
"pg": "^8.23.0",
"quickjs-emscripten": "0.32.0"
},
@@@ -1166,9 +1166,9 @@
}
},
"node_modules/openvibe-shared": {
-- "version": "4.107.4",
-- "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v4.107.4",
-- "integrity": "sha512-tThN0m3KMncEHVcBOvE+ysHjsZTlVIDLG1P+/i2Mulmxfc/AUpebAXhpWWrut50OChZ8o+HeePxsSrPI8mUQOQ==",
++ "version": "4.108.0",
++ "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v4.108.0",
++ "integrity": "sha512-NPzwjxpzGIpFpIm4uw3+VwHfsxbJ2+cD9yuhRzSwolSwqv2kCmwaVtHJ0DA9gKO3K8PCBfIP3UpKzpuSbYEJ+g==",
"license": "MIT",
"dependencies": {
"jsonwebtoken": "^9.0.0",
ddiff --git a/package.json b/package.json
iindex 713b7c0..6d4f26d 100644
---- a/package.json
++++ b/package.json
@@@ -29,7 +29,7 @@
"helmet": "^8.1.0",
"openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.266.0",
"openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.82.0",
-- "openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v4.107.4",
++ "openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v4.108.0",
"pg": "^8.23.0",
"quickjs-emscripten": "0.32.0"
},
ddiff --git a/server/app.js b/server/app.js
iindex 665749e..134d041 100644
---- a/server/app.js
++++ b/server/app.js
@@@ -214,7 +214,7 @@ async function createApp(opts = {}) {
app.get('/robots.txt', (_req, res) => res.type('text/plain').set('Cache-Control', cache.htmlHeaders({ maxAge: 3600 }))
.send(seo.robotsTxt({ sitemaps: [`${config.baseUrl}/sitemap.xml`], disallow: discovery.ROBOTS_DISALLOW })));
app.get('/llms.txt', (_req, res) => res.type('text/plain').set('Cache-Control', cache.htmlHeaders({ maxAge: 3600 }))
-- .send(seo.llmsTxt({ name: SITE_NAME, summary: discovery.SUMMARY, details: discovery.SUMMARY, sections: discovery.llmsSections() })));
++ .send(seo.llmsTxt({ name: SITE_NAME, markdown: true, base: config.baseUrl, summary: discovery.SUMMARY, details: discovery.SUMMARY, sections: discovery.llmsSections(), optional: discovery.llmsOptional() })));
app.get('/llms-full.txt', wrap(async (_req, res) => res.type('text/plain').set('Cache-Control', cache.htmlHeaders({ maxAge: 3600 }))
.send(seo.llmsFull({ site: SITE_NAME, summary: discovery.SUMMARY, base: config.baseUrl, sections: await discovery.llmsFullSections(), maxBytes: 512 * 1024 }))));
app.get('/sitemap.xml', async (_req, res) => {
ddiff --git a/server/discovery.js b/server/discovery.js
iindex 5b9ac53..ca55e66 100644
---- a/server/discovery.js
++++ b/server/discovery.js
@@@ -17,22 +17,29 @@ const ROBOTS_DISALLOW = ['/api/', '/auth/', '/dashboard', '/projects/', '/sites/
const SUMMARY = 'OpenVibe.Space is where people make spaces: small apps on their OpenVibe account, built by OpenVibe AI from a description or started from a template and changed in a browser editor, that sign visitors in with OpenVibe, keep shared lists and private data, and talk to OpenVibe AI. It also serves tested static hosting (immutable deploys, rollback, previews, Git ingest, verified custom domains), open to OpenVibe staff until the public launch. The forum that used to live here is on OpenVibe.Community.';
// ── llms.txt / llms-full.txt (llmstxt.org) ───────────────────
++// AGT-5: each of Space's own pages is linked at its Markdown twin (llmsTxt's markdown: true, base: baseUrl).
++// /spaces and /spaces/new have no twin yet (llms-twins-probe, 11-03), so they keep their URL and say (HTML).
function llmsSections() {
const u = config.baseUrl;
return [
{ title: 'This site', links: [
{ title: 'Home', url: `${u}/`, note: 'spaces and static hosting' },
-- { title: 'Spaces', url: `${u}/spaces`, note: 'the gallery of published spaces and the templates' },
-- { title: 'Make a space', url: `${u}/spaces/new`, note: 'describe it to the AI builder, or start from a template (sign-in required)' },
++ { title: 'Spaces', url: `${u}/spaces`, html: true, note: 'the gallery of published spaces and the templates' },
++ { title: 'Make a space', url: `${u}/spaces/new`, html: true, note: 'describe it to the AI builder, or start from a template (sign-in required)' },
] },
{ title: 'The network', links: [
{ title: 'The forum', url: `${config.communityUrl}/s`, note: 'OpenVibe.Community hosts the forum' },
{ title: 'OpenVibe.Network', url: config.networkUrl, note: 'identity, accounts and sign-in' },
] },
-- { title: 'Machine-readable', links: [
-- { title: 'Sitemap', url: `${u}/sitemap.xml` },
-- { title: 'Full text for language models', url: `${u}/llms-full.txt` },
-- ] },
++ ];
++}
++
++/** llmstxt.org's "## Optional": what an agent may skip when it needs a shorter context. */
++function llmsOptional() {
++ const u = config.baseUrl;
++ return [
++ { title: 'Sitemap', url: `${u}/sitemap.xml` },
++ { title: 'Full text for language models', url: `${u}/llms-full.txt` },
];
}
@@@ -57,4 +64,4 @@ async function sitemapRows(releaseLastmod, db = null) {
return rows;
}
--module.exports = { ROBOTS_DISALLOW, SUMMARY, llmsSections, llmsFullSections, sitemapRows };
++module.exports = { ROBOTS_DISALLOW, SUMMARY, llmsSections, llmsOptional, llmsFullSections, sitemapRows };
ddiff --git a/server/website.js b/server/website.js
iindex cd4d483..49a8ecd 100644
---- a/server/website.js
++++ b/server/website.js
@@@ -49,6 +49,8 @@ function createWebsite({ sitesDomain, publicDir }) {
.send(seo.robotsTxt({ sitemaps: [`${base}/sitemap.xml`], disallow: ['/templates/'] })));
router.get('/sitemap.xml', (_req, res) => res.type('application/xml').set('Cache-Control', PAGE_CACHE)
.send(seo.sitemapXml([{ loc: `${base}/`, changefreq: 'weekly', priority: 1 }])));
++ // AGT-5: the website host's own pages, each at its Markdown twin; none of them answers as .md yet
++ // (llms-twins-probe, 11-03), so they keep their URL and say (HTML). No llms-full.txt is served on this host.
router.get('/llms.txt', (_req, res) => res.type('text/plain').set('Cache-Control', PAGE_CACHE).send(seo.llmsTxt({
name: 'OpenVibe.Website',
summary: pages.DESCRIPTION,
@@@ -56,6 +58,11 @@ function createWebsite({ sitesDomain, publicDir }) {
{ title: 'Start', links: [{ title: 'Website templates', url: `${base}/#templates`, html: true }, { title: 'Build on OpenVibe.Space', url: 'https://openvibe.space/spaces/new', html: true }, { title: 'Put your own computer on the web (OpenVibe Connect)', url: 'https://connect.openvibe.website/', html: true }] },
{ title: 'Templates', links: templates.sites().map((t) => ({ title: t.name, url: `${base}/templates/${t.id}`, html: true, note: t.description })) },
],
++ // llmstxt.org's "## Optional": what an agent may skip when it needs a shorter context.
++ optional: [
++ { title: 'Sitemap', url: `${base}/sitemap.xml` },
++ { title: 'robots.txt', url: `${base}/robots.txt` },
++ ],
})));
router.use('/shared', require('openvibe-shared/serve').handler());
ddiff --git a/test/boot.test.js b/test/boot.test.js
iindex 1d59e5c..c53622c 100644
---- a/test/boot.test.js
++++ b/test/boot.test.js
@@@ -91,7 +91,13 @@ async function check(name, fn) {
assert.match(await robots.text(), /Sitemap:/);
const llms = await fetch(`${base}/llms.txt`);
assert.strictEqual(llms.status, 200);
-- assert.match(await llms.text(), /OpenVibe\.Space/);
++ const text = await llms.text();
++ assert.match(text, /OpenVibe\.Space/);
++ // AGT-5: the home at its Markdown twin, the twin-less pages marked (HTML), Optional last.
++ assert.match(text, new RegExp(`\\[Home\\]\\(http://127\\.0\\.0\\.1:${port}/index\\.md\\)`), 'the home is /index.md');
++ assert.match(text, new RegExp(`\\[Spaces\\]\\(http://127\\.0\\.0\\.1:${port}/spaces\\) \\(HTML\\)`), 'a twin-less page keeps its URL');
++ assert.match(text, /\n## Optional\n/);
++ assert.strictEqual(text.lastIndexOf('\n## '), text.indexOf('\n## Optional'), 'Optional is the last section');
const map = await fetch(`${base}/sitemap.xml`);
assert.strictEqual(map.status, 200);
const xml = await map.text();
ddiff --git a/test/website-home.test.js b/test/website-home.test.js
iindex e6d1152..1958820 100644
---- a/test/website-home.test.js
++++ b/test/website-home.test.js
@@@ -80,6 +80,12 @@ assert.match(hostingSrc, /target\.kind === 'dashboard' \|\| target\.kind === 'we
const llms = (await t.get('openvibe.website', '/llms.txt')).text;
assert.match(llms, /^# OpenVibe\.Website/);
for (const s of templates.sites()) assert.ok(llms.includes(`https://openvibe.website/templates/${s.id}`), s.id);
++ // AGT-5: none of this host's pages answers as .md yet, so each keeps its URL and says (HTML);
++ // Optional is the last section.
++ assert.match(llms, /\[Website templates\]\(https:\/\/openvibe\.website\/#templates\) \(HTML\)/);
++ for (const s of templates.sites()) assert.ok(llms.includes(`https://openvibe.website/templates/${s.id}) (HTML)`), s.id);
++ assert.match(llms, /\n## Optional\n\n[\s\S]*\[Sitemap\]\(https:\/\/openvibe\.website\/sitemap\.xml\)/);
++ assert.strictEqual(llms.lastIndexOf('\n## '), llms.indexOf('\n## Optional'), 'Optional is the last section');
});
await check('the guest start page leads with a website template picked on the front door', async () => {