refactor(catalog): one official-skill-source list and one docs origin

OFFICIAL_SKILL_SOURCES / isOfficialSkillSource live in apps/shared and
replace three hand-kept copies (desktop curation, desktop discovery, the
website skills hero). catalog-data reuses CATALOG_ORIGIN instead of its
own DOCS_ORIGIN copy. The plugin hero's official check reads the tier.
This commit is contained in:
Austin Pickett committed 2026-10-19 22:57:43 -04:00
1 parent 0a203ed9e8
commit 115b0fccd4
5 files changed
+19 -11

No files matched your search

@@ -1,3 +1,5 @@
import { isOfficialSkillSource } from '@hermes/shared'
import curation from '../../../../../shared/src/catalog-curation.json'
const { artwork, featured } = curation
@@ -11,8 +13,6 @@ const images = import.meta.glob<string>('../../../../../../website/static/img/ca
const artworkUrl = (filename: string | undefined) =>
filename ? images[`../../../../../../website/static/img/catalog/${filename}`] ?? null : null
const OFFICIAL_SKILL_SOURCES = ['built-in', 'bundled', 'optional', 'official']
interface CurationRow {
name?: string
category?: string
@@ -24,7 +24,7 @@ interface CurationRow {
/** Official identities whose art the curation file owns: a reassignment there
* must win over a feed published with the old mapping. */
export function curationOwnsArtwork(kind: 'skills' | 'plugins', row: CurationRow): boolean {
return kind === 'skills' ? OFFICIAL_SKILL_SOURCES.includes(row.source ?? '') : row.tier === 'bundled'
return kind === 'skills' ? isOfficialSkillSource(row.source) : row.tier === 'bundled'
}
/** Only first-party catalog identities receive editorial artwork. */
@@ -41,7 +41,7 @@ export function officialCatalogArtwork(kind: 'skills' | 'plugins', row: Curation
* and the site agree once the feed catches up. */
export function curatedFeaturedRank(kind: 'skills' | 'plugins', row: CurationRow): number | undefined {
const key = kind === 'skills'
? (OFFICIAL_SKILL_SOURCES.includes(row.source ?? '') ? `${row.category}/${row.name}` : '')
? (isOfficialSkillSource(row.source) ? `${row.category}/${row.name}` : '')
: (row.tier === 'official' ? row.name ?? '' : '')
const rank = (featured[kind] as string[]).indexOf(key) + 1
@@ -1,6 +1,7 @@
import { skillCatalogInstallIdentifier } from '@hermes/shared'
import { queryOptions, useQuery } from '@tanstack/react-query'
import { CATALOG_ORIGIN } from '@/lib/plugin-catalog'
import { queryClient } from '@/lib/query-client'
import { curatedFeaturedRank, curationOwnsArtwork, officialCatalogArtwork } from './catalog-curation'
@@ -44,10 +45,11 @@ export interface CatalogEntry {
search: string
}
const DOCS_ORIGIN = 'https://hermes-agent.nousresearch.com'
// The public domain redirects here without CORS headers on the redirect.
// Use the docs' actual static host, not GitHub's API or repository endpoints.
const CATALOG_BASE = 'https://nousresearch.github.io/hermes-agent/docs/api'
/** A download with no new bytes for this long is aborted (inactivity, not total time). */
const CATALOG_STALL_MS = 60_000
const text = (value: unknown): string => (typeof value === 'string' ? value : '')
const strings = (value: unknown): string[] => (Array.isArray(value) ? value.filter(v => typeof v === 'string') : [])
@@ -94,7 +96,7 @@ function catalogLinks(kind: CatalogKind, row: any, rawName: string, source: stri
: null,
sourceUrl: webUrl(row.repo || row.sourceUrl),
docsUrl:
webUrl(row.docsUrl) || (text(row.docsPath) ? `${DOCS_ORIGIN}/docs/user-guide/skills/${text(row.docsPath)}` : null),
webUrl(row.docsUrl) || (text(row.docsPath) ? `${CATALOG_ORIGIN}/docs/user-guide/skills/${text(row.docsPath)}` : null),
imageUrl: curationOwnsArtwork(kind, row) ? officialCatalogArtwork(kind, row) : catalogImageUrl(row.image),
screenshots:
kind === 'plugins'
@@ -209,7 +211,7 @@ export async function fetchCatalog(kind: CatalogKind): Promise<CatalogEntry[]> {
const armDeadline = () => {
clearTimeout(timer)
timer = setTimeout(() => controller.abort(), 60_000)
timer = setTimeout(() => controller.abort(), CATALOG_STALL_MS)
}
armDeadline()
@@ -260,7 +262,7 @@ export async function fetchCatalog(kind: CatalogKind): Promise<CatalogEntry[]> {
// Chromium can turn our abort into "The user aborted a request." The
// controller, not that browser-dependent exception name, owns the cause.
if (controller.signal.aborted) {
throw new Error('Catalog download timed out after 60 seconds without progress. Please try again.')
throw new Error(`Catalog download timed out after ${CATALOG_STALL_MS / 1000} seconds without progress. Please try again.`)
}
throw error
+6
View File
@@ -8,6 +8,12 @@
* official entry with art and the most stars. Nothing here reads a download
* or install count; the feed has none. */
/** Skill sources that are first-party (bundled with Hermes or published by
* Nous). One list for the desktop catalog, the website and the hero pick. */
export const OFFICIAL_SKILL_SOURCES: readonly string[] = ['built-in', 'bundled', 'optional', 'official']
export const isOfficialSkillSource = (source: string | null | undefined) => OFFICIAL_SKILL_SOURCES.includes(source ?? '')
export interface FeaturedCandidate {
/** 1-based curated rank from the feed; absent for everything uncurated. */
featured?: number | null
+1 -1
View File
@@ -28,7 +28,7 @@ export type {
UsageModelData
} from './billing-types'
export { groupCatalogPlugins, PLUGIN_CATEGORIES, PLUGIN_CATEGORY_ORDER, sortCatalogPlugins } from './catalog-browse'
export { type FeaturedCandidate, pickFeatured } from './catalog-featured'
export { type FeaturedCandidate, isOfficialSkillSource, OFFICIAL_SKILL_SOURCES, pickFeatured } from './catalog-featured'
export { pluginCatalogInstallUrl, skillCatalogInstallIdentifier, skillCatalogInstallUrl } from './catalog-install'
export {
driveChargeSettlement,
+2 -2
View File
@@ -3,7 +3,7 @@ import Layout from "@theme/Layout";
import Link from "@docusaurus/Link";
import styles from "./styles.module.css";
import CatalogFeatured from "../../components/CatalogFeatured";
import { pickFeatured } from "../../../../apps/shared/src/catalog-featured";
import { isOfficialSkillSource, pickFeatured } from "../../../../apps/shared/src/catalog-featured";
import { skillCatalogInstallIdentifier, skillCatalogInstallUrl } from "../../../../apps/shared/src/catalog-install";
interface Skill {
@@ -801,7 +801,7 @@ export default function SkillsDashboard() {
() =>
pickFeatured(allSkillsLocal, (s) => ({
featured: s.featured,
official: s.source === "built-in" || s.source === "optional",
official: isOfficialSkillSource(s.source),
pictured: Boolean(s.image),
})),
[allSkillsLocal],