openvibers/openvibe.bot Commits

Bot: /updates — what shipped on openvibe.bot (the shared update log) (#117)

OP OpenVibers committed 29 days ago · 3632acbf27 · 3 files, +32 −4

openvibe.bot gets `/updates`: what shipped on the site, from the network's shared update log (`openvibe-shared/frame` `updatesBody` + `shipped.js`), inside the OpenVibe Frame like the front page and the build docs. A site-wide crawl found openvibe.help linking it (404).

- The footer links it (`updates: '/updates'`). It is in the sitemap's public pages, and llms.txt and llms-full.txt carry an entry for it.
- One `<h1>`, a canonical URL, and the front page's CSP (`connect-src` already allows openvibe.network's changelog).

`test/docs-pages.test.js` adds a check for the page, the heading, the script, the canonical URL and the sitemap. The llms, docs-pages, directory, legal, home and try-sim tests pass.

Changes

ddiff --git a/server/web/home.js b/server/web/home.js
iindex 887b194..1280386 100644
---- a/server/web/home.js
++++ b/server/web/home.js
@@@ -15,6 +15,7 @@
const shell = require('openvibe-shared/shell');
const showcase = require('openvibe-shared/showcase');
const ovServe = require('openvibe-shared/serve');
++const frame = require('openvibe-shared/frame');
const appIcon = require('openvibe-shared/app-icon');
const SITE_NAME = 'OpenVibe.Bot';
@@@ -100,13 +101,21 @@ function framePage({ config, title, description, canonicalPath = '/', body, head
return shell.page({
name: SITE_NAME, service: 'bot', lang: 'en', title, siteName: SITE_NAME, description, summary: description,
canonical: `${config.baseUrl}${canonicalPath}`, robots: 'index, follow',
-- navbar: nav, footer: { service: 'bot', variant: 'full', mount: '#ov-footer', brandName: SITE_NAME }, home: '/',
++ navbar: nav, footer: { service: 'bot', variant: 'full', mount: '#ov-footer', brandName: SITE_NAME, updates: '/updates' }, home: '/',
navLinks: [{ label: 'Robots', href: '/robots' }, { label: 'Build', href: '/docs' }],
head: [appIcon.headTags({ site: 'bot' }), '<link rel="manifest" href="/manifest.webmanifest">', head].filter(Boolean).join('\n'),
body: `<div id="navbar-mount"></div>\n${body}`,
});
}
++/** /updates: what shipped on OpenVibe.Bot, the network's shared update log (openvibe-shared/frame updatesBody). */
++function renderUpdates({ config }) {
++ return framePage({
++ config, title: `What shipped on ${SITE_NAME}`, description: `Every change deployed to ${SITE_NAME}, newest first.`, canonicalPath: '/updates',
++ body: `<main id="main" class="page">\n${frame.updatesBody({ service: 'bot', siteName: SITE_NAME })}\n</main>\n<script src="${ovServe.url('shipped.js')}" defer></script>`,
++ });
++}
++
function renderHome({ config, signedIn = false }) {
const nav = {
service: 'bot',
@@@ -140,4 +149,4 @@ ${sections({ signedIn })}
});
}
--module.exports = { renderHome, framePage, HOME_CSP, sections };
++module.exports = { renderHome, renderUpdates, framePage, HOME_CSP, sections };
ddiff --git a/server/web/routes.js b/server/web/routes.js
iindex eea18f2..ddcfd64 100644
---- a/server/web/routes.js
++++ b/server/web/routes.js
@@@ -40,7 +40,7 @@ const { renderPanel, renderEmbedRefused, renderDrivePage, renderPairingPage, ren
const { createMachineScreen } = require('../machine-screen');
const { renderDocsIndex, renderDriversPage, renderProfilesPage, renderAutomationPage, renderComputersPage } = require('./docs');
const { checkProfile } = require('../profiles');
--const { renderHome, HOME_CSP } = require('./home');
++const { renderHome, renderUpdates, HOME_CSP } = require('./home');
const { renderDirectory, renderModels, renderModel, renderRobotPage, renderAddRobot, renderSubmit, renderSubmissions, modelOf } = require('./directory');
const { LICENSES } = require('../submissions');
@@@ -52,7 +52,7 @@ const TRY_PROFILE = loadProfiles().get('sim.rover');
const SITE_TAGLINE = 'An open control panel for robots';
// Every public GET page, for the sitemap. The interactive pages (/robots, /pair/, /panel/) need a session and
// are Disallowed in robots.txt; the API and sign-in are not pages.
--const PUBLIC_PAGES = ['/', '/try', '/robots', '/models', '/install', '/docs', '/docs/drivers', '/docs/profiles', '/docs/automation', '/docs/computers', '/terms', '/privacy', '/dmca'];
++const PUBLIC_PAGES = ['/', '/try', '/robots', '/models', '/install', '/docs', '/docs/drivers', '/docs/profiles', '/docs/automation', '/docs/computers', '/updates', '/terms', '/privacy', '/dmca'];
// The public pages an AI crawler can read, for /llms.txt and /llms-full.txt (llmstxt.org). Same pages as the
// sitemap minus /install (a 302 to the installer script, not a page); no signed-in or per-robot page, nothing
// under the API or /internal. `note` is the one-line llms.txt entry, `text` the page's full text in llms-full.txt,
@@@ -106,6 +106,11 @@ const LLMS_PAGES = [
note: 'The Node\'s computer plugin: levels set on the computer, approvals there, the agent switch and the API.',
text: 'How to let your agents use your own computer through the OpenVibe Node\'s computer plugin. Installed with --computer, it runs as your own account, never root, and its only folder is your home. The level is set on the computer only: observe (screenshots, listing and reading files), ask (programs and writes each approved on the computer with openvibe-node approve), trusted (inside its folders without asking) or full. Programs are argv, never a shell line, time-limited and output-capped. The local stop drops it to observe. The switch Let my agent use this computer on openvibe.bot/machines lets your OpenVibe.Actor agent use it as you. Bot\'s machine commands take exec, fs and screen, and the computer\'s answer comes back in data.',
},
++ {
++ path: '/updates', title: 'What shipped on OpenVibe.Bot',
++ note: 'Every change deployed to OpenVibe.Bot, newest first, from the network\'s shared update log.',
++ text: 'The update log of OpenVibe.Bot: every change deployed to the site, newest first, each line a commit from the OpenVibers repositories linked to the change itself. Batches of changes become Patch notes on openvibe.blog, and the whole network\'s log is on openvibe.network/updates.',
++ },
{
path: '/terms', title: 'Terms of Service',
note: 'The terms for using OpenVibe.Bot.',
@@@ -259,6 +264,8 @@ function createWebRoutes(config, { domain = null, sim = null, onvif = null, limi
const frameCsp = (req, res, next) => { res.setHeader('Content-Security-Policy', HOME_CSP); next(); };
r.get('/docs/docs.css', asset('docs.css', 'text/css'));
r.get('/docs', frameCsp, (req, res) => res.type('html').send(renderDocsIndex({ config })));
++ // What shipped on this site: the shared update log, as on every OpenVibe site.
++ r.get('/updates', frameCsp, (req, res) => res.type('html').send(renderUpdates({ config })));
r.get('/docs/drivers', frameCsp, (req, res) => res.type('html').send(renderDriversPage({ config })));
r.get('/docs/profiles', frameCsp, (req, res) => res.type('html').send(renderProfilesPage({ config })));
r.get('/docs/automation', frameCsp, (req, res) => res.type('html').send(renderAutomationPage({ config })));
ddiff --git a/test/docs-pages.test.js b/test/docs-pages.test.js
iindex bbf9380..5214f1f 100644
---- a/test/docs-pages.test.js
++++ b/test/docs-pages.test.js
@@@ -155,6 +155,18 @@ const { HOME_CSP: SITE_CSP } = require('../server/web/home');
for (const p of ['/docs', '/docs/drivers', '/docs/profiles']) assert.ok(xml.includes(`http://bot.test${p}<`), `sitemap has no ${p}`);
});
++ await check('GET /updates: the shared update log in the Frame, one heading, canonical', async () => {
++ const r = await get('/updates');
++ assert.strictEqual(r.status, 200);
++ assert.strictEqual(r.headers.get('content-security-policy'), SITE_CSP);
++ const html = await r.text();
++ assert.ok(html.includes('<h1>What shipped on OpenVibe.Bot</h1>') && html.includes('data-ov-shipped="log" data-service="bot"'));
++ assert.strictEqual((html.match(/<h1[\s>]/g) || []).length, 1);
++ assert.match(html, /<script src="\/shared\/shipped\.js\?v=[0-9a-f]+" defer><\/script>/);
++ assert.match(html, /<link rel="canonical" href="[^"]+\/updates">/);
++ assert.match(await (await get('/sitemap.xml')).text(), /<loc>[^<]+\/updates<\/loc>/);
++ });
++
await check('GET / links the build pages', async () => {
const html = await (await get('/')).text();
assert.match(html, /href="\/docs"/);