openvibers/openvibe.bot Commits

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

OP OpenVibers committed 10 days ago · 7b5fbbd3b8 · 6 files, +30 −13

* 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 f9f2ebe..9956359 100644
---- a/README.md
++++ b/README.md
@@@ -145,7 +145,7 @@ Sitemap entries use the release commit time for these code-backed pages.
- OpenVibe.Network: user and service tokens, node tokens, pairing when the authority is `network`, and identity resolution (a `@username` to a subject, and subjects to names, for the panel's People card).
- OpenRestream: the WHIP ingest base (`BOT_WHIP_BASE`) devices publish to.
- OpenVibe.Billing: job usage readings go to `billing.usage.record` (`BOT_BILLING_URL`; Bot mints its own Network service token for audience `openvibe.billing` with `billing.usage.record`, and `BOT_BILLING_TOKEN` overrides it); unset, they wait in `run_usage_outbox`.
--- `openvibe-contracts` v0.129.0, `openvibe-sdk` v0.82.0 and `openvibe-shared` v4.107.4 (package.json).
++- `openvibe-contracts` v0.129.0, `openvibe-sdk` v0.82.0 and `openvibe-shared` v4.108.0 (package.json).
- Account export and deletion (ADR-033): Network grants `events.subscription.manage` (openvibe.events) for the two
subscriptions created at boot, then, last and once the release is live, `network.account.export.contribute` and
`network.account.deletion.confirm` (openvibe.network).
@@@ -237,5 +237,5 @@ OpenVibe.Sites no longer generates `deploy/nginx/openvibe.bot.conf` (sites.json
<!-- versions:start -->
- openvibe-contracts: v0.277.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 8060222..da205e4 100644
---- a/STATUS.json
++++ b/STATUS.json
@@@ -7,11 +7,11 @@
"domain": "openvibe.bot (www -> apex; /device and /control WebSockets; /install)",
"contracts": "openvibe-contracts v0.277.0",
"sdk": "openvibe-sdk v0.82.0",
-- "shared": "openvibe-shared v4.107.4",
++ "shared": "openvibe-shared v4.108.0",
"packages": [
"openvibe-contracts v0.277.0",
"openvibe-sdk v0.82.0",
-- "openvibe-shared v4.107.4"
++ "openvibe-shared v4.108.0"
],
"capabilities": [
"bot.robot.read",
ddiff --git a/package-lock.json b/package-lock.json
iindex d659b0f..872ee2d 100644
---- a/package-lock.json
++++ b/package-lock.json
@@@ -15,7 +15,7 @@
"iovalkey": "^0.4.0",
"openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.277.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",
"qrcode-generator": "^2.0.4",
"ws": "^8.18.0"
@@@ -1164,9 +1164,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 90f652c..005eb85 100644
---- a/package.json
++++ b/package.json
@@@ -27,7 +27,7 @@
"iovalkey": "^0.4.0",
"openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.277.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",
"qrcode-generator": "^2.0.4",
"ws": "^8.18.0"
ddiff --git a/server/web/routes.js b/server/web/routes.js
iindex a93c510..23dbb41 100644
---- a/server/web/routes.js
++++ b/server/web/routes.js
@@@ -67,6 +67,8 @@ const PUBLIC_PAGES = ['/', '/try', '/robots', '/models', '/docs', '/docs/drivers
const LLMS_NAME = 'OpenVibe.Bot';
const LLMS_SUMMARY = 'An open control panel for robots: pair a robot with one pasted command and drive it from any '
+ 'browser, with roles, limits and a latched emergency stop on every command.';
++const LLMS_NO_TWIN = new Set(['/try', '/setup/sd-card', '/terms', '/privacy', '/dmca']);
++const LLMS_LEGAL = new Set(['/terms', '/privacy', '/dmca']);
const LLMS_PAGES = [
{
path: '/', title: 'OpenVibe.Bot — drive your robot from any browser',
@@@ -176,9 +178,18 @@ function createWebRoutes(config, { domain = null, sim = null, onvif = null, limi
// /llms.txt and /llms-full.txt (llmstxt.org, openvibe-shared/seo): the public pages as a plain-text map for
// language-model crawlers. Only the pages above; the signed-in pages, the API and /internal are not listed.
// Same headers as robots.txt: text/plain and no explicit Cache-Control.
++ // AGT-5: each page that has a Markdown twin is linked at it (openvibe-shared 4.108.0's markdown mode); the try page,
++ // the SD-card setup and the legal pages have none and are marked HTML. The legal pages, the sitemap and the full text
++ // are ## Optional.
++ const llmsLink = (p) => ({ title: p.title, url: `${config.baseUrl}${p.path}`, note: p.note, ...(LLMS_NO_TWIN.has(p.path) ? { html: true } : {}) });
r.get('/llms.txt', (req, res) => res.type('text/plain').send(seo.llmsTxt({
-- name: LLMS_NAME, summary: LLMS_SUMMARY,
-- sections: [{ title: 'Pages', links: LLMS_PAGES.map((p) => ({ title: p.title, url: `${config.baseUrl}${p.path}`, note: p.note })) }],
++ name: LLMS_NAME, summary: LLMS_SUMMARY, markdown: true, base: config.baseUrl,
++ sections: [{ title: 'Pages', links: LLMS_PAGES.filter((p) => !LLMS_LEGAL.has(p.path)).map(llmsLink) }],
++ optional: [
++ ...LLMS_PAGES.filter((p) => LLMS_LEGAL.has(p.path)).map(llmsLink),
++ { title: 'Sitemap', url: `${config.baseUrl}/sitemap.xml` },
++ { title: 'Full text for language models', url: `${config.baseUrl}/llms-full.txt` },
++ ],
})));
r.get('/llms-full.txt', (req, res) => res.type('text/plain').send(seo.llmsFull({
site: LLMS_NAME, summary: LLMS_SUMMARY, base: config.baseUrl,
ddiff --git a/test/llms.test.js b/test/llms.test.js
iindex f0db3ae..4a24f58 100644
---- a/test/llms.test.js
++++ b/test/llms.test.js
@@@ -26,14 +26,20 @@ const TITLES = ['OpenVibe.Bot — drive your robot from any browser', 'Build for
}
// llms.txt links the front page; llms-full.txt lists it as a page with an absolute URL.
const index = await (await get('/llms.txt')).text();
-- assert.ok(index.includes(`- [${TITLES[0]}](${BASE}/)`), 'llms.txt links the front page');
++ assert.ok(index.includes(`- [${TITLES[0]}](${BASE}/index.md)`), 'llms.txt links the front page at its Markdown twin (AGT-5)');
const full = await (await get('/llms-full.txt')).text();
assert.ok(full.includes(`### ${TITLES[0]}`) && full.includes(`URL: ${BASE}/`), 'llms-full.txt lists the front page');
});
await check('GET /llms.txt: every public page by title and absolute URL, and no API/internal/signed-in path', async () => {
const body = await (await get('/llms.txt')).text();
-- PATHS.forEach((p, i) => assert.ok(body.includes(`- [${TITLES[i]}](${BASE}${p})`), `llms.txt lists ${p}`));
++ // AGT-5: a page with a twin is linked at it; the legal pages have none and are marked HTML, under ## Optional.
++ const legal = ['/terms', '/privacy', '/dmca'];
++ const link = (p) => (legal.includes(p) ? `${BASE}${p}) (HTML)` : `${BASE}${p === '/' ? '/index.md' : `${p}.md`})`);
++ PATHS.forEach((p, i) => assert.ok(body.includes(`- [${TITLES[i]}](${link(p)}`), `llms.txt lists ${p}`));
++ const optional = body.split('\n## Optional\n')[1] || '';
++ assert.ok(legal.every((p) => optional.includes(`${BASE}${p})`)) && optional.includes(`${BASE}/sitemap.xml`), 'the legal pages and the sitemap are optional');
++ assert.ok(body.includes(`(${BASE}/try) (HTML)`), 'the try page has no twin');
for (const bad of ['/api', '/internal', '/panel/']) assert.ok(!body.includes(bad), `llms.txt does not name ${bad}`);
});