openvibers/openvibe.ai Commits
Markdown twins of the home, /stats and /updates (SEO-1) (#283)
Changes
@@@ -170,16 +170,38 @@ function createApp({ config, db, registry, pool, quotas, cache, runs, auth, keystry { homeStats = { at: clock.now(), rows: await statsRows() }; } catch (e) { log.warn(`[home] price and latency rows unavailable: ${e.message}`); }return homeStats.rows || [];}++ // The public pages, one render each: the page and its Markdown twin (SEO-1) are the same render, and the same for++ // everyone (nothing on them is per person).++ const publicPages = {++ '/': async () => home.renderHome({ siteUrl: config.baseUrl, stats: { days: STATS_DAYS, rows: await homeRows() } }),++ '/stats': async () => {++ const now = clock.now();++ return home.renderStats({ siteUrl: config.baseUrl, days: STATS_DAYS, rows: await statsRows(),++ from: new Date(now - STATS_DAYS * 86400000).toISOString().slice(0, 10), to: new Date(now).toISOString().slice(0, 10) });++ },++ '/updates': async () => home.renderUpdates({ siteUrl: config.baseUrl }),++ };++ const agentLayer = require('openvibe-shared/agent-layer');++ // The Link: header that names a public page's twin, as its head does.++ const twinLink = (p) => agentLayer.linkHeaderTags(`${config.baseUrl}${p}`, '/llms.txt').header;++ {++ // A twin request (`/stats.md`, `/index.md`, ?format=markdown, Accept: text/markdown) for a public page.++ const twins = express.Router();++ agentLayer.mount(twins, { descriptor: require('./agent-descriptor'), render: async (p) => (publicPages[p] ? { html: await publicPages[p](), title: null, updated: null } : null) });++ app.use((req, res, next) => {++ const p = String(req.path || '');++ const page = p === '/index.md' ? '/' : p.replace(/\.md$/, '');++ const asked = p.endsWith('.md') || req.query.format === 'markdown' || req.accepts(['html', 'text/markdown']) === 'text/markdown';++ return asked && publicPages[page] && (req.method === 'GET' || req.method === 'HEAD') ? twins(req, res, next) : next();++ });++ }app.get('/stats', async (req, res, next) => {try {-- const now = clock.now();-- const rows = await statsRows();++ const html = await publicPages['/stats']();res.setHeader('Cache-Control', cachePolicy.htmlHeaders());res.setHeader('Content-Security-Policy', home.HOME_CSP);-- res.type('html').send(home.renderStats({ siteUrl: config.baseUrl,-- days: STATS_DAYS, rows,-- from: new Date(now - STATS_DAYS * 86400000).toISOString().slice(0, 10), to: new Date(now).toISOString().slice(0, 10),-- }));++ res.setHeader('Link', twinLink('/stats'));++ res.type('html').send(html);} catch (e) { next(e); }});@@@ -215,17 +237,18 @@ function createApp({ config, db, registry, pool, quotas, cache, runs, auth, keys// GET /updates: what shipped on this site (the shared update log).app.get('/updates', (req, res) => {res.type('html').set('Content-Security-Policy', home.HOME_CSP).set('Cache-Control', 'public, max-age=300')-- .send(home.renderUpdates({ siteUrl: config.baseUrl }));++ .set('Link', twinLink('/updates')).send(home.renderUpdates({ siteUrl: config.baseUrl }));});app.get('/', async (req, res) => {res.vary('Accept');if (req.accepts(['text/plain', 'text/html']) === 'text/html') {-- const rows = await homeRows();++ const html = await publicPages['/']();return res.type('html').set('Content-Security-Policy', home.HOME_CSP).set('Cache-Control', 'private, max-age=300')-- .send(home.renderHome({ siteUrl: config.baseUrl, stats: { days: STATS_DAYS, rows } }));++ .set('Link', twinLink('/'))++ .send(html);}res.type('text/plain').send(['OpenVibe.AI: providers, models, routing, versioned prompt templates and workflows, runs, citations, scoped cache, quotas and audit.',@@@ -308,6 +308,7 @@ ${list.map((r) => `<tr><td data-label="Provider"><code>${esc(r.provider)}</code>siteName: SITE_NAME,description: `Price and latency of every AI model OpenVibe.AI called in the last ${days} days: p50 and p95 latency, success rate, price per million tokens and the free allowance.`,canonical: `${site}/stats`,++ agentLayer: true, llms: '/llms.txt', // the head names the Markdown twin (SEO-1)robots: 'index,follow',navbar: { service: 'ai', apiBase: NETWORK_URL, links: NAV_LINKS, history: { type: 'page', title: SITE_NAME } },footer: FOOTER, home: '/', navLinks: NAV_LINKS,@@@ -332,6 +333,7 @@ function renderUpdates({ siteUrl }) {siteName: SITE_NAME,description: `Every change deployed to ${SITE_NAME}, newest first.`,canonical: `${siteUrl}/updates`,++ agentLayer: true, llms: '/llms.txt',robots: 'index, follow',navbar: { service: 'ai', apiBase: NETWORK_URL, links: NAV_LINKS, history: { type: 'page', title: SITE_NAME } },footer: FOOTER, home: '/', navLinks: NAV_LINKS,@@@ -356,6 +358,7 @@ function renderHome({ siteUrl, stats = null }) {description: DESCRIPTION,summary: SUMMARY,canonical: `${siteUrl}/`,++ agentLayer: true, llms: '/llms.txt',robots: 'index, follow',jsonLd: [seo.jsonLd.website({ name: SITE_NAME, url: site, description: DESCRIPTION }),nnew file mode 100644@@@ -0,0 +1,45 @@++'use strict';++// SEO-1: the public pages' Markdown twins. A twin is the page's own render (the same for everyone: nothing on these++// pages is per person), and the page names it in its head and in the Link: header.++const assert = require('assert');++const { boot, request, suite } = require('./helpers');++++const t = suite('agent-twins');++let h;++const HTML = { accept: 'text/html' };++++t.test('boot', async () => {++ h = await boot({ env: { AI_STUB_FALLBACK: 'false' } });++});++++t.test('each public page names its twin in the head and the Link header, and the twin is the page', async () => {++ for (const [p, twinPath, words] of [['/', '/index.md', 'OpenVibe.AI'], ['/stats', '/stats.md', 'no provider calls in the last 7 days'], ['/updates', '/updates.md', 'OpenVibe.AI']]) {++ const page = await request(h.base, 'GET', p, { headers: HTML });++ assert.strictEqual(page.status, 200, p);++ const head = page.text.match(/<link rel="alternate" type="text\/markdown" href="([^"]+)">/);++ assert.ok(head && head[1].endsWith(twinPath), `${p} names ${twinPath} in the head`);++ assert.ok(page.text.includes('<link rel="describedby" href="/llms.txt">'), `${p} names llms.txt`);++ const link = String(page.headers.get('link') || '');++ assert.ok(link.includes(`<${head[1]}>; rel="alternate"; type="text/markdown"`) && link.includes('</llms.txt>; rel="describedby"'), `${p}: the Link header says what the head says (${link})`);++ const twin = await request(h.base, 'GET', twinPath);++ assert.strictEqual(twin.status, 200, twinPath);++ assert.match(String(twin.headers.get('content-type')), /^text\/markdown/, `${twinPath} is Markdown`);++ assert.ok(twin.text.startsWith('---\ntitle:') && twin.text.includes(words), `${twinPath} is the page's words`);++ assert.ok(!/<(?:script|style)\b/i.test(twin.text.replace(/~~~~[\s\S]*?~~~~/g, '')), `${twinPath} carries no markup`);++ const asked = await request(h.base, 'GET', p, { headers: { accept: 'text/markdown' } });++ assert.match(String(asked.headers.get('content-type')), /^text\/markdown/, `${p} with Accept: text/markdown is the twin`);++ }++});++++t.test('nothing else is a twin: the console, the API, and the API index for curl are as they were', async () => {++ const console_ = await request(h.base, 'GET', '/console.md');++ assert.ok(!/^text\/markdown/.test(String(console_.headers.get('content-type'))), 'the console has no twin');++ const api = await request(h.base, 'GET', '/api/v1/workflows', { headers: { accept: 'text/markdown' } });++ assert.ok(!/^text\/markdown/.test(String(api.headers.get('content-type'))), 'the API is not answered as a twin');++ const index = await request(h.base, 'GET', '/');++ assert.match(String(index.headers.get('content-type')), /^text\/plain/, 'curl still gets the API index');++});++++t.test('stop', async () => { await h.stop(); });++++t.run();
@@ -170,16 +170,38 @@ function createApp({ config, db, registry, pool, quotas, cache, runs, auth, keys
@@ -170,16 +170,38 @@ function createApp({ config, db, registry, pool, quotas, cache, runs, auth, keys
try { homeStats = { at: clock.now(), rows: await statsRows() }; } catch (e) { log.warn(`[home] price and latency rows unavailable: ${e.message}`); }
try { homeStats = { at: clock.now(), rows: await statsRows() }; } catch (e) { log.warn(`[home] price and latency rows unavailable: ${e.message}`); }
return homeStats.rows || [];
return homeStats.rows || [];
}
}
+ // The public pages, one render each: the page and its Markdown twin (SEO-1) are the same render, and the same for
+ // everyone (nothing on them is per person).
+ const publicPages = {
+ '/': async () => home.renderHome({ siteUrl: config.baseUrl, stats: { days: STATS_DAYS, rows: await homeRows() } }),
+ '/stats': async () => {
+ const now = clock.now();
+ return home.renderStats({ siteUrl: config.baseUrl, days: STATS_DAYS, rows: await statsRows(),
+ from: new Date(now - STATS_DAYS * 86400000).toISOString().slice(0, 10), to: new Date(now).toISOString().slice(0, 10) });
+ },
+ '/updates': async () => home.renderUpdates({ siteUrl: config.baseUrl }),
+ };
+ const agentLayer = require('openvibe-shared/agent-layer');
+ // The Link: header that names a public page's twin, as its head does.
+ const twinLink = (p) => agentLayer.linkHeaderTags(`${config.baseUrl}${p}`, '/llms.txt').header;
+ {
+ // A twin request (`/stats.md`, `/index.md`, ?format=markdown, Accept: text/markdown) for a public page.
+ const twins = express.Router();
+ agentLayer.mount(twins, { descriptor: require('./agent-descriptor'), render: async (p) => (publicPages[p] ? { html: await publicPages[p](), title: null, updated: null } : null) });
+ app.use((req, res, next) => {
+ const p = String(req.path || '');
+ const page = p === '/index.md' ? '/' : p.replace(/\.md$/, '');
+ const asked = p.endsWith('.md') || req.query.format === 'markdown' || req.accepts(['html', 'text/markdown']) === 'text/markdown';
+ return asked && publicPages[page] && (req.method === 'GET' || req.method === 'HEAD') ? twins(req, res, next) : next();
+ });
+ }
app.get('/stats', async (req, res, next) => {
app.get('/stats', async (req, res, next) => {
try {
try {
- const now = clock.now();
- const rows = await statsRows();
+ const html = await publicPages['/stats']();
res.setHeader('Cache-Control', cachePolicy.htmlHeaders());
res.setHeader('Cache-Control', cachePolicy.htmlHeaders());
res.setHeader('Content-Security-Policy', home.HOME_CSP);
res.setHeader('Content-Security-Policy', home.HOME_CSP);
- res.type('html').send(home.renderStats({ siteUrl: config.baseUrl,
- days: STATS_DAYS, rows,
- from: new Date(now - STATS_DAYS * 86400000).toISOString().slice(0, 10), to: new Date(now).toISOString().slice(0, 10),
- }));
+ res.setHeader('Link', twinLink('/stats'));
+ res.type('html').send(html);
} catch (e) { next(e); }
} catch (e) { next(e); }
});
});
@@ -215,17 +237,18 @@ function createApp({ config, db, registry, pool, quotas, cache, runs, auth, keys
@@ -215,17 +237,18 @@ function createApp({ config, db, registry, pool, quotas, cache, runs, auth, keys
// GET /updates: what shipped on this site (the shared update log).
// GET /updates: what shipped on this site (the shared update log).
app.get('/updates', (req, res) => {
app.get('/updates', (req, res) => {
res.type('html').set('Content-Security-Policy', home.HOME_CSP).set('Cache-Control', 'public, max-age=300')
res.type('html').set('Content-Security-Policy', home.HOME_CSP).set('Cache-Control', 'public, max-age=300')
- .send(home.renderUpdates({ siteUrl: config.baseUrl }));
+ .set('Link', twinLink('/updates')).send(home.renderUpdates({ siteUrl: config.baseUrl }));
});
});
app.get('/', async (req, res) => {
app.get('/', async (req, res) => {
res.vary('Accept');
res.vary('Accept');
if (req.accepts(['text/plain', 'text/html']) === 'text/html') {
if (req.accepts(['text/plain', 'text/html']) === 'text/html') {
- const rows = await homeRows();
+ const html = await publicPages['/']();
return res.type('html')
return res.type('html')
.set('Content-Security-Policy', home.HOME_CSP)
.set('Content-Security-Policy', home.HOME_CSP)
.set('Cache-Control', 'private, max-age=300')
.set('Cache-Control', 'private, max-age=300')
- .send(home.renderHome({ siteUrl: config.baseUrl, stats: { days: STATS_DAYS, rows } }));
+ .set('Link', twinLink('/'))
+ .send(html);
}
}
res.type('text/plain').send([
res.type('text/plain').send([
'OpenVibe.AI: providers, models, routing, versioned prompt templates and workflows, runs, citations, scoped cache, quotas and audit.',
'OpenVibe.AI: providers, models, routing, versioned prompt templates and workflows, runs, citations, scoped cache, quotas and audit.',
@@ -308,6 +308,7 @@ ${list.map((r) => `<tr><td data-label="Provider"><code>${esc(r.provider)}</code>
@@ -308,6 +308,7 @@ ${list.map((r) => `<tr><td data-label="Provider"><code>${esc(r.provider)}</code>
siteName: SITE_NAME,
siteName: SITE_NAME,
description: `Price and latency of every AI model OpenVibe.AI called in the last ${days} days: p50 and p95 latency, success rate, price per million tokens and the free allowance.`,
description: `Price and latency of every AI model OpenVibe.AI called in the last ${days} days: p50 and p95 latency, success rate, price per million tokens and the free allowance.`,
canonical: `${site}/stats`,
canonical: `${site}/stats`,
+ agentLayer: true, llms: '/llms.txt', // the head names the Markdown twin (SEO-1)
robots: 'index,follow',
robots: 'index,follow',
navbar: { service: 'ai', apiBase: NETWORK_URL, links: NAV_LINKS, history: { type: 'page', title: SITE_NAME } },
navbar: { service: 'ai', apiBase: NETWORK_URL, links: NAV_LINKS, history: { type: 'page', title: SITE_NAME } },
footer: FOOTER, home: '/', navLinks: NAV_LINKS,
footer: FOOTER, home: '/', navLinks: NAV_LINKS,
@@ -332,6 +333,7 @@ function renderUpdates({ siteUrl }) {
@@ -332,6 +333,7 @@ function renderUpdates({ siteUrl }) {
siteName: SITE_NAME,
siteName: SITE_NAME,
description: `Every change deployed to ${SITE_NAME}, newest first.`,
description: `Every change deployed to ${SITE_NAME}, newest first.`,
canonical: `${siteUrl}/updates`,
canonical: `${siteUrl}/updates`,
+ agentLayer: true, llms: '/llms.txt',
robots: 'index, follow',
robots: 'index, follow',
navbar: { service: 'ai', apiBase: NETWORK_URL, links: NAV_LINKS, history: { type: 'page', title: SITE_NAME } },
navbar: { service: 'ai', apiBase: NETWORK_URL, links: NAV_LINKS, history: { type: 'page', title: SITE_NAME } },
footer: FOOTER, home: '/', navLinks: NAV_LINKS,
footer: FOOTER, home: '/', navLinks: NAV_LINKS,
@@ -356,6 +358,7 @@ function renderHome({ siteUrl, stats = null }) {
@@ -356,6 +358,7 @@ function renderHome({ siteUrl, stats = null }) {
description: DESCRIPTION,
description: DESCRIPTION,
summary: SUMMARY,
summary: SUMMARY,
canonical: `${siteUrl}/`,
canonical: `${siteUrl}/`,
+ agentLayer: true, llms: '/llms.txt',
robots: 'index, follow',
robots: 'index, follow',
jsonLd: [
jsonLd: [
seo.jsonLd.website({ name: SITE_NAME, url: site, description: DESCRIPTION }),
seo.jsonLd.website({ name: SITE_NAME, url: site, description: DESCRIPTION }),
new file mode 100644
new file mode 100644
@@ -0,0 +1,45 @@
@@ -0,0 +1,45 @@
+'use strict';
+// SEO-1: the public pages' Markdown twins. A twin is the page's own render (the same for everyone: nothing on these
+// pages is per person), and the page names it in its head and in the Link: header.
+const assert = require('assert');
+const { boot, request, suite } = require('./helpers');
+
+const t = suite('agent-twins');
+let h;
+const HTML = { accept: 'text/html' };
+
+t.test('boot', async () => {
+ h = await boot({ env: { AI_STUB_FALLBACK: 'false' } });
+});
+
+t.test('each public page names its twin in the head and the Link header, and the twin is the page', async () => {
+ for (const [p, twinPath, words] of [['/', '/index.md', 'OpenVibe.AI'], ['/stats', '/stats.md', 'no provider calls in the last 7 days'], ['/updates', '/updates.md', 'OpenVibe.AI']]) {
+ const page = await request(h.base, 'GET', p, { headers: HTML });
+ assert.strictEqual(page.status, 200, p);
+ const head = page.text.match(/<link rel="alternate" type="text\/markdown" href="([^"]+)">/);
+ assert.ok(head && head[1].endsWith(twinPath), `${p} names ${twinPath} in the head`);
+ assert.ok(page.text.includes('<link rel="describedby" href="/llms.txt">'), `${p} names llms.txt`);
+ const link = String(page.headers.get('link') || '');
+ assert.ok(link.includes(`<${head[1]}>; rel="alternate"; type="text/markdown"`) && link.includes('</llms.txt>; rel="describedby"'), `${p}: the Link header says what the head says (${link})`);
+ const twin = await request(h.base, 'GET', twinPath);
+ assert.strictEqual(twin.status, 200, twinPath);
+ assert.match(String(twin.headers.get('content-type')), /^text\/markdown/, `${twinPath} is Markdown`);
+ assert.ok(twin.text.startsWith('---\ntitle:') && twin.text.includes(words), `${twinPath} is the page's words`);
+ assert.ok(!/<(?:script|style)\b/i.test(twin.text.replace(/~~~~[\s\S]*?~~~~/g, '')), `${twinPath} carries no markup`);
+ const asked = await request(h.base, 'GET', p, { headers: { accept: 'text/markdown' } });
+ assert.match(String(asked.headers.get('content-type')), /^text\/markdown/, `${p} with Accept: text/markdown is the twin`);
+ }
+});
+
+t.test('nothing else is a twin: the console, the API, and the API index for curl are as they were', async () => {
+ const console_ = await request(h.base, 'GET', '/console.md');
+ assert.ok(!/^text\/markdown/.test(String(console_.headers.get('content-type'))), 'the console has no twin');
+ const api = await request(h.base, 'GET', '/api/v1/workflows', { headers: { accept: 'text/markdown' } });
+ assert.ok(!/^text\/markdown/.test(String(api.headers.get('content-type'))), 'the API is not answered as a twin');
+ const index = await request(h.base, 'GET', '/');
+ assert.match(String(index.headers.get('content-type')), /^text\/plain/, 'curl still gets the API index');
+});
+
+t.test('stop', async () => { await h.stop(); });
+
+t.run();