openvibers/openvibe.codes Commits
Docs: /docs/tools lists every OpenVibe tool with an API from Tools' live registry (loopback, cached 5 min; how it runs, who may call it, JSON or files), with the run API, jobs, OpenAPI and SDK; linked from /docs and the sitemap
Changes
@@@ -42,6 +42,7 @@ function createDocsRoutes(ctx) {<li><a href="/docs/capabilities"><strong>Capabilities</strong></a><span>${docs.capabilities.length} capabilities; ${grantable} can be granted to apps</span></li><li><a href="/docs/events"><strong>Events</strong></a><span>${docs.events.length} event types services declare they produce</span></li><li><a href="/docs/services"><strong>Services</strong></a><span>the registry as OpenVibe.Network reports it, with health</span></li>++<li><a href="/docs/tools"><strong>Tools API</strong></a><span>every OpenVibe tool you can call from code, from the live registry</span></li><li><a href="/docs/sdk"><strong>SDK</strong></a><span>${docs.sdk.length} modules of openvibe-sdk from their type definitions</span></li><li><a href="/policy/rfc"><strong>Decisions</strong></a><span>${docs.adrs.length} architecture decision records</span></li></ul>@@@ -189,6 +190,37 @@ ${table(['Service', 'Status (manifest)', 'Domains'], docs.services.map((s) => [h});});++ // ── Tools (live registry from OpenVibe.Tools, ADR-027) ──────────++ const TOOLS_URL = (process.env.OV_TOOLS_INTERNAL_URL || 'http://127.0.0.1:4001').replace(/\/$/, '');++ let toolsCache = { at: 0, list: null };++ r.get('/tools', async (req, res) => {++ let list = toolsCache.list; let problem = null;++ if (!list || Date.now() - toolsCache.at > 300_000) {++ try {++ const out = await fetch(`${TOOLS_URL}/api/v1/tools`, { headers: { Host: 'openvibe.tools', Accept: 'application/json' }, signal: AbortSignal.timeout(4000) });++ if (!out.ok) throw new Error(`Tools answered ${out.status}`);++ list = (await out.json()).tools || [];++ toolsCache = { at: Date.now(), list };++ } catch (err) { problem = { code: 'codes.tools_unavailable', detail: err.message }; }++ }++ const api = (list || []).filter((t) => t.api && t.status !== 'unavailable');++ const families = [...new Set(api.map((t) => t.family))].sort();++ send(res, problem && !list ? 502 : 200, {++ index: true, cache: 'public, max-age=60', viewer: req.viewer, config, path: req.originalUrl,++ title: 'Tools API',++ crumbs: [{ label: 'Docs', href: '/docs' }, { label: 'Tools' }],++ body: html`<h1>Tools API</h1>++<p>${api.length} tools on <a href="https://openvibe.tools">OpenVibe.Tools</a> can be called from code: <code>POST https://openvibe.tools/api/v1/tools/{id}/run</code> (capability <code>tools.tool.run</code>; anonymous calls run on the lowest tier), long work as jobs at <code>/api/v1/jobs/{id}</code>. OpenAPI: <a href="https://openvibe.tools/api/v1/openapi.json">openapi.json</a> · guide: <a href="https://openvibe.tools/developers">openvibe.tools/developers</a> · SDK: <code>openvibe-sdk/tools</code>.</p>++${problem && !list ? html`${problemBox(problem, { title: 'The Tools registry could not be read' })}` : ''}++${families.map((f) => html`<h2>${f}</h2>${table(['Tool', 'Runs as', 'Access', 'Inputs'], api.filter((t) => t.family === f).map((t) => [++ html`<a href="${t.docs || `https://openvibe.tools/tool/${t.id}`}"><strong>${t.name}</strong></a><br><code>${t.id}</code>`,++ t.execution === 'job' ? 'job' : 'direct',++ t.auth && t.auth.anonymous ? 'anonymous or token' : (t.auth && t.auth.capability === 'tools.net.probe' ? 'partner token (tools.net.probe)' : 'session or token'),++ t.files ? `files (${t.files.min}-${t.files.max})` : 'JSON',++ ]))}`)}`,++ });++ });++// ── SDK ─────────────────────────────────────────────────r.get('/sdk', (req, res) => {page(req, res, {@@@ -72,7 +72,7 @@ function createPageRoutes(ctx) {<li>Publish release metadata with a <a href="/manifests/validate">validated manifest</a>; rotate or revoke credentials any time.</li></ol><h2>Reference</h2>--<p>Generated from <code>openvibe-contracts v${docs.contractsVersion}</code> and <code>openvibe-sdk v${docs.sdkVersion}</code>: <a href="/docs/contracts">contracts</a> · <a href="/docs/capabilities">capabilities</a> · <a href="/docs/events">events</a> · <a href="/docs/services">services</a> · <a href="/docs/sdk">SDK</a>.</p>++<p>Generated from <code>openvibe-contracts v${docs.contractsVersion}</code> and <code>openvibe-sdk v${docs.sdkVersion}</code>: <a href="/docs/contracts">contracts</a> · <a href="/docs/capabilities">capabilities</a> · <a href="/docs/events">events</a> · <a href="/docs/services">services</a> · <a href="/docs/tools">tools</a> · <a href="/docs/sdk">SDK</a>.</p><h2>Recent releases</h2>${table(['App', 'Kind', 'Version', 'Status', 'Trust', 'Published'], recent.map((x) => [html`<a href="/apps/${x.app_id}">${x.name}</a>`, x.kind, html`<a href="/releases/${x.id}">${x.version}</a>`, x.status, x.trust.tier, time(x.published_at),@@@ -267,7 +267,7 @@ ${signedIn ? releaseActions(req, rel) : ''}].join('\n'));});r.get('/sitemap.xml', (req, res) => {-- const urls = ['/', '/docs', '/docs/contracts', '/docs/capabilities', '/docs/events', '/docs/services', '/docs/sdk', '/oauth', '/tools/webhooks', '/manifests/validate',++ const urls = ['/', '/docs', '/docs/contracts', '/docs/capabilities', '/docs/events', '/docs/services', '/docs/tools', '/docs/sdk', '/oauth', '/tools/webhooks', '/manifests/validate','/policy', '/policy/rfc', '/policy/compatibility', '/policy/licensing', '/policy/transparency',...governance.filter((g) => !g.draft).map((g) => `/policy/${g.slug}`),...docs.contracts.map((c) => `/docs/contracts/${c.id}`), ...docs.capabilities.map((c) => `/docs/capabilities/${c.id}`),
@@ -42,6 +42,7 @@ function createDocsRoutes(ctx) {
@@ -42,6 +42,7 @@ function createDocsRoutes(ctx) {
<li><a href="/docs/capabilities"><strong>Capabilities</strong></a><span>${docs.capabilities.length} capabilities; ${grantable} can be granted to apps</span></li>
<li><a href="/docs/capabilities"><strong>Capabilities</strong></a><span>${docs.capabilities.length} capabilities; ${grantable} can be granted to apps</span></li>
<li><a href="/docs/events"><strong>Events</strong></a><span>${docs.events.length} event types services declare they produce</span></li>
<li><a href="/docs/events"><strong>Events</strong></a><span>${docs.events.length} event types services declare they produce</span></li>
<li><a href="/docs/services"><strong>Services</strong></a><span>the registry as OpenVibe.Network reports it, with health</span></li>
<li><a href="/docs/services"><strong>Services</strong></a><span>the registry as OpenVibe.Network reports it, with health</span></li>
+<li><a href="/docs/tools"><strong>Tools API</strong></a><span>every OpenVibe tool you can call from code, from the live registry</span></li>
<li><a href="/docs/sdk"><strong>SDK</strong></a><span>${docs.sdk.length} modules of openvibe-sdk from their type definitions</span></li>
<li><a href="/docs/sdk"><strong>SDK</strong></a><span>${docs.sdk.length} modules of openvibe-sdk from their type definitions</span></li>
<li><a href="/policy/rfc"><strong>Decisions</strong></a><span>${docs.adrs.length} architecture decision records</span></li>
<li><a href="/policy/rfc"><strong>Decisions</strong></a><span>${docs.adrs.length} architecture decision records</span></li>
</ul>
</ul>
@@ -189,6 +190,37 @@ ${table(['Service', 'Status (manifest)', 'Domains'], docs.services.map((s) => [h
@@ -189,6 +190,37 @@ ${table(['Service', 'Status (manifest)', 'Domains'], docs.services.map((s) => [h
});
});
});
});
+ // ── Tools (live registry from OpenVibe.Tools, ADR-027) ──────────
+ const TOOLS_URL = (process.env.OV_TOOLS_INTERNAL_URL || 'http://127.0.0.1:4001').replace(/\/$/, '');
+ let toolsCache = { at: 0, list: null };
+ r.get('/tools', async (req, res) => {
+ let list = toolsCache.list; let problem = null;
+ if (!list || Date.now() - toolsCache.at > 300_000) {
+ try {
+ const out = await fetch(`${TOOLS_URL}/api/v1/tools`, { headers: { Host: 'openvibe.tools', Accept: 'application/json' }, signal: AbortSignal.timeout(4000) });
+ if (!out.ok) throw new Error(`Tools answered ${out.status}`);
+ list = (await out.json()).tools || [];
+ toolsCache = { at: Date.now(), list };
+ } catch (err) { problem = { code: 'codes.tools_unavailable', detail: err.message }; }
+ }
+ const api = (list || []).filter((t) => t.api && t.status !== 'unavailable');
+ const families = [...new Set(api.map((t) => t.family))].sort();
+ send(res, problem && !list ? 502 : 200, {
+ index: true, cache: 'public, max-age=60', viewer: req.viewer, config, path: req.originalUrl,
+ title: 'Tools API',
+ crumbs: [{ label: 'Docs', href: '/docs' }, { label: 'Tools' }],
+ body: html`<h1>Tools API</h1>
+<p>${api.length} tools on <a href="https://openvibe.tools">OpenVibe.Tools</a> can be called from code: <code>POST https://openvibe.tools/api/v1/tools/{id}/run</code> (capability <code>tools.tool.run</code>; anonymous calls run on the lowest tier), long work as jobs at <code>/api/v1/jobs/{id}</code>. OpenAPI: <a href="https://openvibe.tools/api/v1/openapi.json">openapi.json</a> · guide: <a href="https://openvibe.tools/developers">openvibe.tools/developers</a> · SDK: <code>openvibe-sdk/tools</code>.</p>
+${problem && !list ? html`${problemBox(problem, { title: 'The Tools registry could not be read' })}` : ''}
+${families.map((f) => html`<h2>${f}</h2>${table(['Tool', 'Runs as', 'Access', 'Inputs'], api.filter((t) => t.family === f).map((t) => [
+ html`<a href="${t.docs || `https://openvibe.tools/tool/${t.id}`}"><strong>${t.name}</strong></a><br><code>${t.id}</code>`,
+ t.execution === 'job' ? 'job' : 'direct',
+ t.auth && t.auth.anonymous ? 'anonymous or token' : (t.auth && t.auth.capability === 'tools.net.probe' ? 'partner token (tools.net.probe)' : 'session or token'),
+ t.files ? `files (${t.files.min}-${t.files.max})` : 'JSON',
+ ]))}`)}`,
+ });
+ });
+
// ── SDK ─────────────────────────────────────────────────
// ── SDK ─────────────────────────────────────────────────
r.get('/sdk', (req, res) => {
r.get('/sdk', (req, res) => {
page(req, res, {
page(req, res, {
@@ -72,7 +72,7 @@ function createPageRoutes(ctx) {
@@ -72,7 +72,7 @@ function createPageRoutes(ctx) {
<li>Publish release metadata with a <a href="/manifests/validate">validated manifest</a>; rotate or revoke credentials any time.</li>
<li>Publish release metadata with a <a href="/manifests/validate">validated manifest</a>; rotate or revoke credentials any time.</li>
</ol>
</ol>
<h2>Reference</h2>
<h2>Reference</h2>
-<p>Generated from <code>openvibe-contracts v${docs.contractsVersion}</code> and <code>openvibe-sdk v${docs.sdkVersion}</code>: <a href="/docs/contracts">contracts</a> · <a href="/docs/capabilities">capabilities</a> · <a href="/docs/events">events</a> · <a href="/docs/services">services</a> · <a href="/docs/sdk">SDK</a>.</p>
+<p>Generated from <code>openvibe-contracts v${docs.contractsVersion}</code> and <code>openvibe-sdk v${docs.sdkVersion}</code>: <a href="/docs/contracts">contracts</a> · <a href="/docs/capabilities">capabilities</a> · <a href="/docs/events">events</a> · <a href="/docs/services">services</a> · <a href="/docs/tools">tools</a> · <a href="/docs/sdk">SDK</a>.</p>
<h2>Recent releases</h2>
<h2>Recent releases</h2>
${table(['App', 'Kind', 'Version', 'Status', 'Trust', 'Published'], recent.map((x) => [
${table(['App', 'Kind', 'Version', 'Status', 'Trust', 'Published'], recent.map((x) => [
html`<a href="/apps/${x.app_id}">${x.name}</a>`, x.kind, html`<a href="/releases/${x.id}">${x.version}</a>`, x.status, x.trust.tier, time(x.published_at),
html`<a href="/apps/${x.app_id}">${x.name}</a>`, x.kind, html`<a href="/releases/${x.id}">${x.version}</a>`, x.status, x.trust.tier, time(x.published_at),
@@ -267,7 +267,7 @@ ${signedIn ? releaseActions(req, rel) : ''}
@@ -267,7 +267,7 @@ ${signedIn ? releaseActions(req, rel) : ''}
].join('\n'));
].join('\n'));
});
});
r.get('/sitemap.xml', (req, res) => {
r.get('/sitemap.xml', (req, res) => {
- const urls = ['/', '/docs', '/docs/contracts', '/docs/capabilities', '/docs/events', '/docs/services', '/docs/sdk', '/oauth', '/tools/webhooks', '/manifests/validate',
+ const urls = ['/', '/docs', '/docs/contracts', '/docs/capabilities', '/docs/events', '/docs/services', '/docs/tools', '/docs/sdk', '/oauth', '/tools/webhooks', '/manifests/validate',
'/policy', '/policy/rfc', '/policy/compatibility', '/policy/licensing', '/policy/transparency',
'/policy', '/policy/rfc', '/policy/compatibility', '/policy/licensing', '/policy/transparency',
...governance.filter((g) => !g.draft).map((g) => `/policy/${g.slug}`),
...governance.filter((g) => !g.draft).map((g) => `/policy/${g.slug}`),
...docs.contracts.map((c) => `/docs/contracts/${c.id}`), ...docs.capabilities.map((c) => `/docs/capabilities/${c.id}`),
...docs.contracts.map((c) => `/docs/contracts/${c.id}`), ...docs.capabilities.map((c) => `/docs/capabilities/${c.id}`),