Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
106 changes: 105 additions & 1 deletion agent-edge.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export const AGENT_SURFACE = {
"name": "Starboard",
"url": "https://starboard.codevetter.com",
"llmsFullTxt": "# Starboard — full agent brief\n\nGitHub stars organizer with semantic search — sub-product of CodeVetter for repo intelligence.\n\n## Index\n\n# Starboard\n\nGitHub stars organizer, repository discovery surface, and semantic search product.\n\n## Public without sign-in\n\n- Browse and search the seeded Discover corpus\n- Inspect detected tool and framework intelligence\n- Read the product history, scope, privacy policy, and terms\n\n## With GitHub sign-in\n\n- Sync and organize personal stars with tags and collections\n- Search starred repositories and receive fleet-aware recommendations\n- Track maintainer signals, alerts, and shareable insight reports\n\n## Agent entrypoints\n\n- https://starboard.codevetter.com/llms.txt\n- https://starboard.codevetter.com/api/ai\n- https://starboard.codevetter.com/index.md\n\n## Product links\n\n- Home: https://starboard.codevetter.com/ — Product\n- Discover: https://starboard.codevetter.com/discover — Browse and search the seeded public repository corpus\n- Tools: https://starboard.codevetter.com/tools — Detected tool and framework intelligence\n- Changelog: https://starboard.codevetter.com/changelog — Verified product history\n- About: https://starboard.codevetter.com/about — Product purpose and scope\n- Privacy: https://starboard.codevetter.com/privacy — Privacy and data handling\n- Terms: https://starboard.codevetter.com/terms — Terms of use\n\n## Machine surfaces\n\n- https://starboard.codevetter.com/llms.txt\n- https://starboard.codevetter.com/llms-full.txt\n- https://starboard.codevetter.com/api/ai\n- https://starboard.codevetter.com/index.md\n- https://starboard.codevetter.com/sitemap.xml\n- https://starboard.codevetter.com/robots.txt\n\n## Contact\n\n- Owner: https://sarthakagrawal.dev\n- Agent email for directory verification: sarthakagrawal@agentmail.to\n",
"llmsTxt": "# Starboard\n\n> GitHub stars organizer with semantic search — sub-product of CodeVetter for repo intelligence.\n\n## Product\n\n- [Home](https://starboard.codevetter.com/): Product\n- [Discover](https://starboard.codevetter.com/discover): Browse and search the seeded public repository corpus\n- [Tools](https://starboard.codevetter.com/tools): Detected tool and framework intelligence\n- [Changelog](https://starboard.codevetter.com/changelog): Verified product history\n- [About](https://starboard.codevetter.com/about): Product purpose and scope\n- [Privacy](https://starboard.codevetter.com/privacy): Privacy and data handling\n- [Terms](https://starboard.codevetter.com/terms): Terms of use\n\n## Machine surfaces\n\n- [Agent catalog](https://starboard.codevetter.com/api/ai): JSON inventory of public surfaces\n- [Homepage markdown](https://starboard.codevetter.com/index.md): Product brief without JS\n- [This index](https://starboard.codevetter.com/llms.txt)\n",
"llmsTxt": "# Starboard\n\n> Project-aware GitHub repository discovery and evidence-backed tool intelligence.\n\n## When to use this\n\n- Discovering and browsing GitHub repositories by semantic similarity, tags, or tool category\n- Finding alternatives to a known tool or framework via the seeded public corpus\n- Organizing personal GitHub stars with tags, collections, and semantic search (requires sign-in)\n- Getting evidence-backed tool intelligence and framework detection for a project\n- Answering questions about what tools or libraries a repository uses\n\n## Product\n\n- [Home](https://starboard.codevetter.com/): Product\n- [Project Preview](https://starboard.codevetter.com/project-preview): Preview cataloged recommendations before sign-in; new lookups require GitHub sign-in\n- [Discover](https://starboard.codevetter.com/discover): Browse and search the seeded public repository corpus\n- [Tools](https://starboard.codevetter.com/tools): Detected tool and framework intelligence\n- [Changelog](https://starboard.codevetter.com/changelog): Verified product history\n- [About](https://starboard.codevetter.com/about): Product purpose and scope\n- [Privacy](https://starboard.codevetter.com/privacy): Privacy and data handling\n- [Terms](https://starboard.codevetter.com/terms): Terms of use\n\n## Machine surfaces\n\n- [Agent catalog](https://starboard.codevetter.com/api/ai): JSON inventory of public surfaces\n- [OpenAPI spec](https://starboard.codevetter.com/openapi.json): Machine-readable API contract\n- [Homepage markdown](https://starboard.codevetter.com/index.md): Product brief without JS\n- [This index](https://starboard.codevetter.com/llms.txt)\n",
"indexMd": "# Starboard\n\nGitHub stars organizer, repository discovery surface, and semantic search product.\n\n## Public without sign-in\n\n- Browse and search the seeded Discover corpus\n- Inspect detected tool and framework intelligence\n- Read the product history, scope, privacy policy, and terms\n\n## With GitHub sign-in\n\n- Sync and organize personal stars with tags and collections\n- Search starred repositories and receive fleet-aware recommendations\n- Track maintainer signals, alerts, and shareable insight reports\n\n## Agent entrypoints\n\n- https://starboard.codevetter.com/llms.txt\n- https://starboard.codevetter.com/api/ai\n- https://starboard.codevetter.com/index.md\n",
"catalog": {
"name": "Starboard",
Expand All @@ -24,6 +24,7 @@ export const AGENT_SURFACE = {
"llmsFull": "https://starboard.codevetter.com/llms-full.txt",
"sitemap": "https://starboard.codevetter.com/sitemap.xml",
"robots": "https://starboard.codevetter.com/robots.txt",
"openapi": "https://starboard.codevetter.com/openapi.json",
"markdown": {
"suffix": ".md",
"negotiation": true
Expand Down Expand Up @@ -114,6 +115,7 @@ export function handleAgentEdge(request) {
sitemap: AGENT_SURFACE.catalog.sitemap
? String(AGENT_SURFACE.catalog.sitemap).replace(AGENT_SURFACE.url, url.origin)
: `${url.origin}/sitemap.xml`,
openapi: `${url.origin}/openapi.json`,
surfaces: (AGENT_SURFACE.catalog.surfaces || []).map((s) => ({
...s,
url: s.url ? String(s.url).replace(AGENT_SURFACE.url, url.origin) : s.url,
Expand All @@ -123,6 +125,15 @@ export function handleAgentEdge(request) {
return json(catalog);
}

if (path === '/openapi.json') {
return json(openapiSpecForOrigin(url.origin));
}

// JSON error for unknown /api/* paths
if (path.startsWith('/api/')) {
return jsonError(404, 'not_found', `Unknown API path: ${path}`, path);
}

// Homepage markdown negotiation
if ((path === '/' || path === '') && wantsMarkdown(request)) {
return text(AGENT_SURFACE.indexMd, 'text/markdown; charset=utf-8', {
Expand All @@ -131,6 +142,11 @@ export function handleAgentEdge(request) {
});
}

// Agent-friendly 404: markdown body for Accept: text/markdown
if (wantsMarkdown(request) && !path.includes('.')) {
return markdown404(path, url.origin);
}

return null;
}

Expand All @@ -141,6 +157,94 @@ function wantsMarkdown(request) {
return accept.indexOf('text/markdown') < accept.indexOf('text/html');
}

function openapiSpecForOrigin(origin) {
return {
openapi: '3.1.0',
info: {
title: 'Starboard public API',
version: '1.0.0',
description:
'Project-aware GitHub repository discovery and evidence-backed tool intelligence.',
contact: { name: 'Starboard', url: origin },
},
servers: [{ url: origin }],
tags: [{ name: 'agent-surfaces', description: 'Machine-readable public surfaces' }],
paths: {
'/api/ai': {
get: {
operationId: 'getAgentCatalog',
tags: ['agent-surfaces'],
summary: 'Agent catalog',
responses: {
200: { description: 'Agent catalog JSON', content: { 'application/json': {} } },
},
},
},
'/llms.txt': {
get: {
operationId: 'getLlmsTxt',
tags: ['agent-surfaces'],
summary: 'llms.txt index',
responses: { 200: { description: 'Markdown index', content: { 'text/plain': {} } } },
},
},
'/sitemap.xml': {
get: {
operationId: 'getSitemap',
tags: ['agent-surfaces'],
summary: 'Sitemap',
responses: { 200: { description: 'XML sitemap', content: { 'application/xml': {} } } },
},
},
'/openapi.json': {
get: {
operationId: 'getOpenApiSpec',
tags: ['agent-surfaces'],
summary: 'OpenAPI specification',
description: 'This document.',
responses: {
200: { description: 'OpenAPI 3.1 spec', content: { 'application/json': {} } },
},
},
},
},
};
}

function jsonError(status, code, message, path) {
return new Response(JSON.stringify({ error: { code, message, path } }), {
status,
headers: {
'Content-Type': 'application/json; charset=utf-8',
'Cache-Control': 'no-store',
'Access-Control-Allow-Origin': '*',
},
});
}

function markdown404(pathname, origin) {
const body = `# 404 — Not Found

\`${pathname}\` does not exist on this site.

## Where to look next

- [Home](${origin}/)
- [Sitemap](${origin}/sitemap.xml)
- [Agent index](${origin}/llms.txt)
- [Agent catalog (JSON)](${origin}/api/ai)
- [OpenAPI spec](${origin}/openapi.json)
`;
return new Response(body, {
status: 404,
headers: {
'Content-Type': 'text/markdown; charset=utf-8',
'Cache-Control': 'no-store',
'X-Content-Type-Options': 'nosniff',
},
});
}

function text(body, type, extra = {}) {
return new Response(body, {
status: 200,
Expand Down
4 changes: 2 additions & 2 deletions next.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@ const securityHeaders = [
key: 'Content-Security-Policy',
value: [
"default-src 'self'",
"script-src 'self' 'unsafe-inline' 'unsafe-eval' https://us-assets.i.posthog.com", // unsafe-inline/eval required by Next.js
"script-src 'self' 'unsafe-inline' 'unsafe-eval' https://us-assets.i.posthog.com https://www.clarity.ms", // unsafe-inline/eval required by Next.js
"style-src 'self' 'unsafe-inline'",
"img-src 'self' data: https://avatars.githubusercontent.com https://github.com",
"connect-src 'self' https://api.github.com https://us.i.posthog.com https://us-assets.i.posthog.com",
"connect-src 'self' https://api.github.com https://us.i.posthog.com https://us-assets.i.posthog.com https://www.clarity.ms",
"frame-ancestors 'none'",
].join('; '),
},
Expand Down
1 change: 1 addition & 0 deletions public/api-ai.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"llmsFull": "https://starboard.codevetter.com/llms-full.txt",
"sitemap": "https://starboard.codevetter.com/sitemap.xml",
"robots": "https://starboard.codevetter.com/robots.txt",
"openapi": "https://starboard.codevetter.com/openapi.json",
"markdown": {
"suffix": ".md",
"negotiation": true
Expand Down
9 changes: 9 additions & 0 deletions public/llms.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,14 @@

> Project-aware GitHub repository discovery and evidence-backed tool intelligence.

## When to use this

- Discovering and browsing GitHub repositories by semantic similarity, tags, or tool category
- Finding alternatives to a known tool or framework via the seeded public corpus
- Organizing personal GitHub stars with tags, collections, and semantic search (requires sign-in)
- Getting evidence-backed tool intelligence and framework detection for a project
- Answering questions about what tools or libraries a repository uses

## Product

- [Home](https://starboard.codevetter.com/): Product
Expand All @@ -16,5 +24,6 @@
## Machine surfaces

- [Agent catalog](https://starboard.codevetter.com/api/ai): JSON inventory of public surfaces
- [OpenAPI spec](https://starboard.codevetter.com/openapi.json): Machine-readable API contract
- [Homepage markdown](https://starboard.codevetter.com/index.md): Product brief without JS
- [This index](https://starboard.codevetter.com/llms.txt)
6 changes: 6 additions & 0 deletions src/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,12 @@ export default function RootLayout({
}}
/>
{/* fleet-jsonld:end */}
{/* Microsoft Clarity — session replay + heatmaps (additive; PostHog stays) */}
<script
dangerouslySetInnerHTML={{
__html: `(function(c,l,a,r,i,t,y){c[a]=c[a]||function(){(c[a].q=c[a].q||[]).push(arguments)};t=l.createElement(r);t.async=1;t.src="https://www.clarity.ms/tag/y6bv3iipqv";y=l.getElementsByTagName(r)[0];y.parentNode.insertBefore(t,y);})(window,document,"clarity","script","y6bv3iipqv");window.clarity("set","project_id","starboard");`,
}}
/>
<script
id="starboard-name-shim"
dangerouslySetInnerHTML={{
Expand Down
18 changes: 18 additions & 0 deletions worker.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,21 @@ const worker = {
// Only cache 2xx HTML responses — never error pages or redirects.
const contentType = response.headers.get('content-type') ?? '';
if (response.status !== 200 || !contentType.includes('text/html')) {
// Add Vary: Accept to non-200 HTML responses (e.g. 404) so caches
// distinguish markdown vs HTML negotiation.
if (contentType.includes('text/html')) {
const headers = new Headers(response.headers);
const vary = headers.get('vary');
headers.set(
'vary',
vary ? `${vary}, Accept, Accept-Encoding` : 'Accept, Accept-Encoding'
);
return new Response(response.body, {
status: response.status,
statusText: response.statusText,
headers,
});
}
return response;
}

Expand All @@ -167,6 +182,9 @@ const worker = {
const body = await response.arrayBuffer();
const headers = new Headers(response.headers);
headers.set('Cache-Control', CACHE_CONTROL);
// Add Vary: Accept for HTML pages that have markdown alternates
const vary = headers.get('vary');
headers.set('vary', vary ? `${vary}, Accept, Accept-Encoding` : 'Accept, Accept-Encoding');

const cacheable = new Response(body, {
status: response.status,
Expand Down
Loading