openvibers/openvibe.tools Commits
/developers has a Markdown twin; llms.txt links it (#210)
Changes
@@@ -1,7 +1,7 @@'use strict';// The Markdown twins of openvibe.tools' public pages (openvibe-shared/agent-layer): the directory, every family,// /all-tools and each tool's page. They are the same pageIndex/pageFamily/pageTool/pageAll renderers the site serves,--// so a twin cannot drift from its page. The noindex search page and the API pages have no twin.++// so a twin cannot drift from its page; /developers too. The noindex search page and the API pages have no twin.const site = require('./site');const registry = require('../registry');@@@ -13,6 +13,8 @@ function createAgentPages({ opens = null } = {}) {return { html: site.pageIndex(top) };}if (p === '/all-tools') return { html: site.pageAll() };++ // The API guide (11-03: llms.txt listed it as HTML; for an agent it is the page that matters most).++ if (p === '/developers') return { html: site.pageDevelopers() };if (p.startsWith('/tool/')) {const t = registry.get().tools.find((x) => x.id === p.slice('/tool/'.length));return t ? { html: site.pageTool(t) } : null;@@@ -683,7 +683,7 @@ function llmsTxtBody() {// is on another subdomain, so its link keeps its URL; /developers has no twin yet (llms-twins-probe, 11-03), so// it keeps its URL and says (HTML). The API stays in a main section: it is what an agent came for.return seo.llmsTxt({ name: NAME, markdown: true, base: SITE, summary: llmsSummary(),-- sections: [{ title: 'API', links: [{ title: 'Developer guide', url: SITE + '/developers', html: true, note: 'run any tool from code' }, { title: 'OpenAPI 3.1', url: SITE + '/api/v1/openapi.json' }, { title: 'Tool registry (JSON)', url: SITE + '/api/v1/tools' }] }].concat(llmsSections()),++ sections: [{ title: 'API', links: [{ title: 'Developer guide', url: SITE + '/developers', note: 'run any tool from code' }, { title: 'OpenAPI 3.1', url: SITE + '/api/v1/openapi.json' }, { title: 'Tool registry (JSON)', url: SITE + '/api/v1/tools' }] }].concat(llmsSections()),// llmstxt.org's "## Optional": what an agent may skip when it needs a shorter context.optional: [{ title: 'Full text (llms-full.txt)', url: SITE + '/llms-full.txt', note: 'the public pages above as one plain-text document' },@@@ -736,4 +736,4 @@ function createSiteRouter({ opens = null } = {}) {const renderNotFound = () => pageSearch('').replace('<h1>Search tools</h1>', '<h1>That page is not here</h1>').replace(/<title>[^<]*<\/title>/, '<title>Page not found | OpenVibe.Tools</title>');--module.exports = { createSiteRouter, pageIndex, pageFamily, pageTool, pageAll, search, sitemapEntries, renderNotFound, SITE, NAME };++module.exports = { createSiteRouter, pageIndex, pageFamily, pageTool, pageAll, pageDevelopers, search, sitemapEntries, renderNotFound, SITE, NAME };@@@ -48,9 +48,11 @@ function serve(app) {assert.ok(!body.includes('/auth/') && !body.includes('/search') && !body.includes('Disallow'), 'no private paths');// llms.txt points at the full document.assert.ok(idx.includes('/llms-full.txt'), 'llms.txt lists llms-full.txt');-- // AGT-5: each of the site's own pages at its Markdown twin, the twin-less /developers marked (HTML), an++ // AGT-5: each of the site's own pages at its Markdown twin (the developer guide has one since 11-03), an// Optional section last. A tool on another subdomain keeps its URL.-- assert.ok(idx.includes('[Developer guide](https://openvibe.tools/developers) (HTML)'), 'the twin-less guide keeps its URL');++ assert.ok(idx.includes('[Developer guide](https://openvibe.tools/developers.md)'), 'the guide links its twin');++ const devTwin = await require('../server/pages/agent-pages').createAgentPages().renderPublicPage('/developers');++ assert.ok(devTwin && /Tools API for developers/.test(devTwin.html), 'and the twin renders the guide');assert.ok(idx.includes('[Sitemap](https://openvibe.tools/sitemap.xml)'), 'the sitemap is listed in Optional');assert.match(idx, /\n## Optional\n/);assert.strictEqual(idx.lastIndexOf('\n## '), idx.indexOf('\n## Optional'), 'Optional is the last section');
@@ -1,7 +1,7 @@
@@ -1,7 +1,7 @@
'use strict';
'use strict';
// The Markdown twins of openvibe.tools' public pages (openvibe-shared/agent-layer): the directory, every family,
// The Markdown twins of openvibe.tools' public pages (openvibe-shared/agent-layer): the directory, every family,
// /all-tools and each tool's page. They are the same pageIndex/pageFamily/pageTool/pageAll renderers the site serves,
// /all-tools and each tool's page. They are the same pageIndex/pageFamily/pageTool/pageAll renderers the site serves,
-// so a twin cannot drift from its page. The noindex search page and the API pages have no twin.
+// so a twin cannot drift from its page; /developers too. The noindex search page and the API pages have no twin.
const site = require('./site');
const site = require('./site');
const registry = require('../registry');
const registry = require('../registry');
@@ -13,6 +13,8 @@ function createAgentPages({ opens = null } = {}) {
@@ -13,6 +13,8 @@ function createAgentPages({ opens = null } = {}) {
return { html: site.pageIndex(top) };
return { html: site.pageIndex(top) };
}
}
if (p === '/all-tools') return { html: site.pageAll() };
if (p === '/all-tools') return { html: site.pageAll() };
+ // The API guide (11-03: llms.txt listed it as HTML; for an agent it is the page that matters most).
+ if (p === '/developers') return { html: site.pageDevelopers() };
if (p.startsWith('/tool/')) {
if (p.startsWith('/tool/')) {
const t = registry.get().tools.find((x) => x.id === p.slice('/tool/'.length));
const t = registry.get().tools.find((x) => x.id === p.slice('/tool/'.length));
return t ? { html: site.pageTool(t) } : null;
return t ? { html: site.pageTool(t) } : null;
@@ -683,7 +683,7 @@ function llmsTxtBody() {
@@ -683,7 +683,7 @@ function llmsTxtBody() {
// is on another subdomain, so its link keeps its URL; /developers has no twin yet (llms-twins-probe, 11-03), so
// is on another subdomain, so its link keeps its URL; /developers has no twin yet (llms-twins-probe, 11-03), so
// it keeps its URL and says (HTML). The API stays in a main section: it is what an agent came for.
// it keeps its URL and says (HTML). The API stays in a main section: it is what an agent came for.
return seo.llmsTxt({ name: NAME, markdown: true, base: SITE, summary: llmsSummary(),
return seo.llmsTxt({ name: NAME, markdown: true, base: SITE, summary: llmsSummary(),
- sections: [{ title: 'API', links: [{ title: 'Developer guide', url: SITE + '/developers', html: true, note: 'run any tool from code' }, { title: 'OpenAPI 3.1', url: SITE + '/api/v1/openapi.json' }, { title: 'Tool registry (JSON)', url: SITE + '/api/v1/tools' }] }].concat(llmsSections()),
+ sections: [{ title: 'API', links: [{ title: 'Developer guide', url: SITE + '/developers', note: 'run any tool from code' }, { title: 'OpenAPI 3.1', url: SITE + '/api/v1/openapi.json' }, { title: 'Tool registry (JSON)', url: SITE + '/api/v1/tools' }] }].concat(llmsSections()),
// llmstxt.org's "## Optional": what an agent may skip when it needs a shorter context.
// llmstxt.org's "## Optional": what an agent may skip when it needs a shorter context.
optional: [
optional: [
{ title: 'Full text (llms-full.txt)', url: SITE + '/llms-full.txt', note: 'the public pages above as one plain-text document' },
{ title: 'Full text (llms-full.txt)', url: SITE + '/llms-full.txt', note: 'the public pages above as one plain-text document' },
@@ -736,4 +736,4 @@ function createSiteRouter({ opens = null } = {}) {
@@ -736,4 +736,4 @@ function createSiteRouter({ opens = null } = {}) {
const renderNotFound = () => pageSearch('').replace('<h1>Search tools</h1>', '<h1>That page is not here</h1>').replace(/<title>[^<]*<\/title>/, '<title>Page not found | OpenVibe.Tools</title>');
const renderNotFound = () => pageSearch('').replace('<h1>Search tools</h1>', '<h1>That page is not here</h1>').replace(/<title>[^<]*<\/title>/, '<title>Page not found | OpenVibe.Tools</title>');
-module.exports = { createSiteRouter, pageIndex, pageFamily, pageTool, pageAll, search, sitemapEntries, renderNotFound, SITE, NAME };
+module.exports = { createSiteRouter, pageIndex, pageFamily, pageTool, pageAll, pageDevelopers, search, sitemapEntries, renderNotFound, SITE, NAME };
@@ -48,9 +48,11 @@ function serve(app) {
@@ -48,9 +48,11 @@ function serve(app) {
assert.ok(!body.includes('/auth/') && !body.includes('/search') && !body.includes('Disallow'), 'no private paths');
assert.ok(!body.includes('/auth/') && !body.includes('/search') && !body.includes('Disallow'), 'no private paths');
// llms.txt points at the full document.
// llms.txt points at the full document.
assert.ok(idx.includes('/llms-full.txt'), 'llms.txt lists llms-full.txt');
assert.ok(idx.includes('/llms-full.txt'), 'llms.txt lists llms-full.txt');
- // AGT-5: each of the site's own pages at its Markdown twin, the twin-less /developers marked (HTML), an
+ // AGT-5: each of the site's own pages at its Markdown twin (the developer guide has one since 11-03), an
// Optional section last. A tool on another subdomain keeps its URL.
// Optional section last. A tool on another subdomain keeps its URL.
- assert.ok(idx.includes('[Developer guide](https://openvibe.tools/developers) (HTML)'), 'the twin-less guide keeps its URL');
+ assert.ok(idx.includes('[Developer guide](https://openvibe.tools/developers.md)'), 'the guide links its twin');
+ const devTwin = await require('../server/pages/agent-pages').createAgentPages().renderPublicPage('/developers');
+ assert.ok(devTwin && /Tools API for developers/.test(devTwin.html), 'and the twin renders the guide');
assert.ok(idx.includes('[Sitemap](https://openvibe.tools/sitemap.xml)'), 'the sitemap is listed in Optional');
assert.ok(idx.includes('[Sitemap](https://openvibe.tools/sitemap.xml)'), 'the sitemap is listed in Optional');
assert.match(idx, /\n## Optional\n/);
assert.match(idx, /\n## Optional\n/);
assert.strictEqual(idx.lastIndexOf('\n## '), idx.indexOf('\n## Optional'), 'Optional is the last section');
assert.strictEqual(idx.lastIndexOf('\n## '), idx.indexOf('\n## Optional'), 'Optional is the last section');