openvibers/openvibe.tours Commits
openvibe-shared v4.108.0 (was v4.107.4) (#66)
Changes
@@@ -177,6 +177,6 @@ AGPL-3.0-only. See [LICENSE](LICENSE).<!-- versions:start -->- openvibe-contracts: v0.274.0- openvibe-sdk: v0.82.0--- openvibe-shared: v4.107.4++- openvibe-shared: v4.108.0- openvibe-publishing: v1.4.1<!-- versions:end -->@@@ -10,12 +10,12 @@"domain": "openvibe.tours","contracts": "openvibe-contracts v0.274.0","sdk": "openvibe-sdk v0.82.0",-- "shared": "openvibe-shared v4.107.4",++ "shared": "openvibe-shared v4.108.0","packages": ["openvibe-contracts v0.274.0","openvibe-publishing v1.4.1","openvibe-sdk v0.82.0",-- "openvibe-shared v4.107.4"++ "openvibe-shared v4.108.0"],"summary": "OpenVibe.Tours is the travel room. The top 100 cities come from Wikidata coordinates and Wikivoyage pages; places are OpenStreetMap tourism=* through one bounded Overpass query per city; self-guided routes are generated from Wikivoyage See lists, marked as generated, and cite every stop. IRL streams, VODs and clips come from OpenVibe.Live's public list, stays from OpenVibe.Rent's public listings. There is no flight API key in v1: the form links the official airline-neutral search with the city pair filled in and no affiliate. A signed-in person can make a trip with a city, dates, saved places, one route and stays, and share it read-only. No route needs a service token; the source jobs are off under TOURS_DRILL.","works": [@@@ -18,7 +18,7 @@"openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.274.0","openvibe-publishing": "https://codeload.github.com/OpenVibers/OpenVibe.Publishing/tar.gz/refs/tags/v1.4.1","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"},"devDependencies": {@@@ -1217,9 +1217,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",@@@ -26,7 +26,7 @@"openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.274.0","openvibe-publishing": "https://codeload.github.com/OpenVibers/OpenVibe.Publishing/tar.gz/refs/tags/v1.4.1","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"},"engines": {@@@ -60,6 +60,10 @@ function createDiscoveryRoutes(ctx) {r.get('/llms.txt', (_req, res) => {res.type('text/plain').set('Cache-Control', TEXT).send(seo.llmsTxt({name: SITE_NAME,++ // Each of this site's own pages is linked at its Markdown twin (AGT-5; openvibe-shared 4.108.0 leaves++ // files, /api/ and /.well-known/ paths and other sites' links as they are).++ markdown: true,++ base: site,summary: DESCRIPTION,details: 'GET /api/v1/cities?limit=&offset=&q= lists the top cities; GET /api/v1/cities/:slug adds its places, routes, live tours and stays. GET /api/v1/places/:id and GET /api/v1/routes/:id return one place or one route with its ordered stops. GET /api/v1/live returns IRL tours now and recent IRL VODs. Trips are a signed-in person\'s own: the tours_at session cookie or a Network Bearer token. Every place is OpenStreetMap (ODbL) with its osm.org link and fetch time; route stops cite the Wikivoyage page (CC BY-SA); city images say their Commons licence. No route needs a service token. Flights have no API key in v1; the form only links the official airline-neutral search, with no affiliate.',sections: [@@@ -69,21 +73,22 @@ function createDiscoveryRoutes(ctx) {{ title: 'Routes', url: abs('/routes') },{ title: 'Live tours', url: abs('/live') },{ title: 'The API', url: abs('/developers') },-- { title: 'OpenAPI', url: abs('/api/v1/openapi.json') },] },{ title: 'Machine-readable', links: [{ title: 'Cities (JSON)', url: abs('/api/v1/cities') },{ title: 'Live (JSON)', url: abs('/api/v1/live') },-- { title: 'Sitemap', url: abs('/sitemap.xml') },-- { title: 'Full text for language models', url: abs('/llms-full.txt') },-- { title: 'Release metadata (JSON)', url: abs('/release.json') },-- ] },-- { title: 'Elsewhere', links: [-- { title: 'OpenVibe.Live', url: 'https://openvibe.live', note: 'the IRL streams' },-- { title: 'OpenVibe.Rent', url: 'https://openvibe.rent', note: 'the listings' },-- { title: 'OpenVibe.Network', url: 'https://openvibe.network', note: 'accounts and sign-in' },++ { title: 'OpenAPI', url: abs('/api/v1/openapi.json') },] },],++ // llmstxt.org's "## Optional": what an agent may skip when it needs a shorter context.++ optional: [++ { title: 'Sitemap', url: abs('/sitemap.xml') },++ { title: 'Full text for language models', url: abs('/llms-full.txt') },++ { title: 'Release metadata (JSON)', url: abs('/release.json') },++ { title: 'OpenVibe.Live', url: 'https://openvibe.live', note: 'the IRL streams' },++ { title: 'OpenVibe.Rent', url: 'https://openvibe.rent', note: 'the listings' },++ { title: 'OpenVibe.Network', url: 'https://openvibe.network', note: 'accounts and sign-in' },++ ],}));});@@@ -125,6 +125,19 @@ const { seed } = require('./helpers/seed');const llms = await t.get('/llms.txt');assert.strictEqual(llms.status, 200);assert.match(llms.text, /OpenVibe\.Tours/);++ // AGT-5: the home at /index.md, each page at its twin, Optional last.++ assert.match(llms.text, /\[OpenVibe\.Tours\]\(https:\/\/openvibe\.tours\/index\.md\)/, 'the home is /index.md');++ assert.match(llms.text, /\[Cities\]\(https:\/\/openvibe\.tours\/cities\.md\)/, 'a page is its twin');++ assert.match(llms.text, /\[Cities \(JSON\)\]\(https:\/\/openvibe\.tours\/api\/v1\/cities\)/, 'an API URL keeps its URL');++ assert.match(llms.text, /\n## Optional\n/);++ assert.match(llms.text, /\n## Optional\n\n[\s\S]*\[Sitemap\]\(https:\/\/openvibe\.tours\/sitemap\.xml\)/);++ assert.strictEqual(llms.text.lastIndexOf('\n## '), llms.text.indexOf('\n## Optional'), 'Optional is the last section');++ const own = [...llms.text.matchAll(/\]\((https:\/\/openvibe\.tours[^)\s]*)\)( \(HTML\))?/g)];++ assert.ok(own.length > 5, String(own.length));++ for (const [, url, html] of own) {++ const p = new URL(url).pathname;++ assert.ok(p.endsWith('.md') || /\.[a-z0-9]+$/.test(p) || p.startsWith('/api/') || html, `${url} is a twin, a file or marked (HTML)`);++ }const full = await t.get('/llms-full.txt');assert.strictEqual(full.status, 200);assert.match(full.text, /Lisbon/);
@@ -177,6 +177,6 @@ AGPL-3.0-only. See [LICENSE](LICENSE).
@@ -177,6 +177,6 @@ AGPL-3.0-only. See [LICENSE](LICENSE).
<!-- versions:start -->
<!-- versions:start -->
- openvibe-contracts: v0.274.0
- openvibe-contracts: v0.274.0
- openvibe-sdk: v0.82.0
- openvibe-sdk: v0.82.0
-- openvibe-shared: v4.107.4
+- openvibe-shared: v4.108.0
- openvibe-publishing: v1.4.1
- openvibe-publishing: v1.4.1
<!-- versions:end -->
<!-- versions:end -->
@@ -10,12 +10,12 @@
@@ -10,12 +10,12 @@
"domain": "openvibe.tours",
"domain": "openvibe.tours",
"contracts": "openvibe-contracts v0.274.0",
"contracts": "openvibe-contracts v0.274.0",
"sdk": "openvibe-sdk v0.82.0",
"sdk": "openvibe-sdk v0.82.0",
- "shared": "openvibe-shared v4.107.4",
+ "shared": "openvibe-shared v4.108.0",
"packages": [
"packages": [
"openvibe-contracts v0.274.0",
"openvibe-contracts v0.274.0",
"openvibe-publishing v1.4.1",
"openvibe-publishing v1.4.1",
"openvibe-sdk v0.82.0",
"openvibe-sdk v0.82.0",
- "openvibe-shared v4.107.4"
+ "openvibe-shared v4.108.0"
],
],
"summary": "OpenVibe.Tours is the travel room. The top 100 cities come from Wikidata coordinates and Wikivoyage pages; places are OpenStreetMap tourism=* through one bounded Overpass query per city; self-guided routes are generated from Wikivoyage See lists, marked as generated, and cite every stop. IRL streams, VODs and clips come from OpenVibe.Live's public list, stays from OpenVibe.Rent's public listings. There is no flight API key in v1: the form links the official airline-neutral search with the city pair filled in and no affiliate. A signed-in person can make a trip with a city, dates, saved places, one route and stays, and share it read-only. No route needs a service token; the source jobs are off under TOURS_DRILL.",
"summary": "OpenVibe.Tours is the travel room. The top 100 cities come from Wikidata coordinates and Wikivoyage pages; places are OpenStreetMap tourism=* through one bounded Overpass query per city; self-guided routes are generated from Wikivoyage See lists, marked as generated, and cite every stop. IRL streams, VODs and clips come from OpenVibe.Live's public list, stays from OpenVibe.Rent's public listings. There is no flight API key in v1: the form links the official airline-neutral search with the city pair filled in and no affiliate. A signed-in person can make a trip with a city, dates, saved places, one route and stays, and share it read-only. No route needs a service token; the source jobs are off under TOURS_DRILL.",
"works": [
"works": [
@@ -18,7 +18,7 @@
@@ -18,7 +18,7 @@
"openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.274.0",
"openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.274.0",
"openvibe-publishing": "https://codeload.github.com/OpenVibers/OpenVibe.Publishing/tar.gz/refs/tags/v1.4.1",
"openvibe-publishing": "https://codeload.github.com/OpenVibers/OpenVibe.Publishing/tar.gz/refs/tags/v1.4.1",
"openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.82.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"
"pg": "^8.23.0"
},
},
"devDependencies": {
"devDependencies": {
@@ -1217,9 +1217,9 @@
@@ -1217,9 +1217,9 @@
}
}
},
},
"node_modules/openvibe-shared": {
"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",
"license": "MIT",
"dependencies": {
"dependencies": {
"jsonwebtoken": "^9.0.0",
"jsonwebtoken": "^9.0.0",
@@ -26,7 +26,7 @@
@@ -26,7 +26,7 @@
"openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.274.0",
"openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.274.0",
"openvibe-publishing": "https://codeload.github.com/OpenVibers/OpenVibe.Publishing/tar.gz/refs/tags/v1.4.1",
"openvibe-publishing": "https://codeload.github.com/OpenVibers/OpenVibe.Publishing/tar.gz/refs/tags/v1.4.1",
"openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.82.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"
"pg": "^8.23.0"
},
},
"engines": {
"engines": {
@@ -60,6 +60,10 @@ function createDiscoveryRoutes(ctx) {
@@ -60,6 +60,10 @@ function createDiscoveryRoutes(ctx) {
r.get('/llms.txt', (_req, res) => {
r.get('/llms.txt', (_req, res) => {
res.type('text/plain').set('Cache-Control', TEXT).send(seo.llmsTxt({
res.type('text/plain').set('Cache-Control', TEXT).send(seo.llmsTxt({
name: SITE_NAME,
name: SITE_NAME,
+ // Each of this site's own pages is linked at its Markdown twin (AGT-5; openvibe-shared 4.108.0 leaves
+ // files, /api/ and /.well-known/ paths and other sites' links as they are).
+ markdown: true,
+ base: site,
summary: DESCRIPTION,
summary: DESCRIPTION,
details: 'GET /api/v1/cities?limit=&offset=&q= lists the top cities; GET /api/v1/cities/:slug adds its places, routes, live tours and stays. GET /api/v1/places/:id and GET /api/v1/routes/:id return one place or one route with its ordered stops. GET /api/v1/live returns IRL tours now and recent IRL VODs. Trips are a signed-in person\'s own: the tours_at session cookie or a Network Bearer token. Every place is OpenStreetMap (ODbL) with its osm.org link and fetch time; route stops cite the Wikivoyage page (CC BY-SA); city images say their Commons licence. No route needs a service token. Flights have no API key in v1; the form only links the official airline-neutral search, with no affiliate.',
details: 'GET /api/v1/cities?limit=&offset=&q= lists the top cities; GET /api/v1/cities/:slug adds its places, routes, live tours and stays. GET /api/v1/places/:id and GET /api/v1/routes/:id return one place or one route with its ordered stops. GET /api/v1/live returns IRL tours now and recent IRL VODs. Trips are a signed-in person\'s own: the tours_at session cookie or a Network Bearer token. Every place is OpenStreetMap (ODbL) with its osm.org link and fetch time; route stops cite the Wikivoyage page (CC BY-SA); city images say their Commons licence. No route needs a service token. Flights have no API key in v1; the form only links the official airline-neutral search, with no affiliate.',
sections: [
sections: [
@@ -69,21 +73,22 @@ function createDiscoveryRoutes(ctx) {
@@ -69,21 +73,22 @@ function createDiscoveryRoutes(ctx) {
{ title: 'Routes', url: abs('/routes') },
{ title: 'Routes', url: abs('/routes') },
{ title: 'Live tours', url: abs('/live') },
{ title: 'Live tours', url: abs('/live') },
{ title: 'The API', url: abs('/developers') },
{ title: 'The API', url: abs('/developers') },
- { title: 'OpenAPI', url: abs('/api/v1/openapi.json') },
] },
] },
{ title: 'Machine-readable', links: [
{ title: 'Machine-readable', links: [
{ title: 'Cities (JSON)', url: abs('/api/v1/cities') },
{ title: 'Cities (JSON)', url: abs('/api/v1/cities') },
{ title: 'Live (JSON)', url: abs('/api/v1/live') },
{ title: 'Live (JSON)', url: abs('/api/v1/live') },
- { title: 'Sitemap', url: abs('/sitemap.xml') },
- { title: 'Full text for language models', url: abs('/llms-full.txt') },
- { title: 'Release metadata (JSON)', url: abs('/release.json') },
- ] },
- { title: 'Elsewhere', links: [
- { title: 'OpenVibe.Live', url: 'https://openvibe.live', note: 'the IRL streams' },
- { title: 'OpenVibe.Rent', url: 'https://openvibe.rent', note: 'the listings' },
- { title: 'OpenVibe.Network', url: 'https://openvibe.network', note: 'accounts and sign-in' },
+ { title: 'OpenAPI', url: abs('/api/v1/openapi.json') },
] },
] },
],
],
+ // llmstxt.org's "## Optional": what an agent may skip when it needs a shorter context.
+ optional: [
+ { title: 'Sitemap', url: abs('/sitemap.xml') },
+ { title: 'Full text for language models', url: abs('/llms-full.txt') },
+ { title: 'Release metadata (JSON)', url: abs('/release.json') },
+ { title: 'OpenVibe.Live', url: 'https://openvibe.live', note: 'the IRL streams' },
+ { title: 'OpenVibe.Rent', url: 'https://openvibe.rent', note: 'the listings' },
+ { title: 'OpenVibe.Network', url: 'https://openvibe.network', note: 'accounts and sign-in' },
+ ],
}));
}));
});
});
@@ -125,6 +125,19 @@ const { seed } = require('./helpers/seed');
@@ -125,6 +125,19 @@ const { seed } = require('./helpers/seed');
const llms = await t.get('/llms.txt');
const llms = await t.get('/llms.txt');
assert.strictEqual(llms.status, 200);
assert.strictEqual(llms.status, 200);
assert.match(llms.text, /OpenVibe\.Tours/);
assert.match(llms.text, /OpenVibe\.Tours/);
+ // AGT-5: the home at /index.md, each page at its twin, Optional last.
+ assert.match(llms.text, /\[OpenVibe\.Tours\]\(https:\/\/openvibe\.tours\/index\.md\)/, 'the home is /index.md');
+ assert.match(llms.text, /\[Cities\]\(https:\/\/openvibe\.tours\/cities\.md\)/, 'a page is its twin');
+ assert.match(llms.text, /\[Cities \(JSON\)\]\(https:\/\/openvibe\.tours\/api\/v1\/cities\)/, 'an API URL keeps its URL');
+ assert.match(llms.text, /\n## Optional\n/);
+ assert.match(llms.text, /\n## Optional\n\n[\s\S]*\[Sitemap\]\(https:\/\/openvibe\.tours\/sitemap\.xml\)/);
+ assert.strictEqual(llms.text.lastIndexOf('\n## '), llms.text.indexOf('\n## Optional'), 'Optional is the last section');
+ const own = [...llms.text.matchAll(/\]\((https:\/\/openvibe\.tours[^)\s]*)\)( \(HTML\))?/g)];
+ assert.ok(own.length > 5, String(own.length));
+ for (const [, url, html] of own) {
+ const p = new URL(url).pathname;
+ assert.ok(p.endsWith('.md') || /\.[a-z0-9]+$/.test(p) || p.startsWith('/api/') || html, `${url} is a twin, a file or marked (HTML)`);
+ }
const full = await t.get('/llms-full.txt');
const full = await t.get('/llms-full.txt');
assert.strictEqual(full.status, 200);
assert.strictEqual(full.status, 200);
assert.match(full.text, /Lisbon/);
assert.match(full.text, /Lisbon/);