mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-11-06 05:17:15 +00:00
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:
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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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],
|
||||
|
||||
Reference in new issue
Block a user