Merge pull request #94 from y574444354/feat/server-refactor-90331-2
feat(favorite): optimize list caching and expand MCP type support
This commit is contained in:
commit
1c22fe8d2a
|
|
@ -8,8 +8,14 @@ import { clearSkillCaches } from '../../skills/loadSkillsDir.js'
|
||||||
import { parseFrontmatter } from '../../utils/frontmatterParser.js'
|
import { parseFrontmatter } from '../../utils/frontmatterParser.js'
|
||||||
import type { McpServerConfig } from '../../services/mcp/types.js'
|
import type { McpServerConfig } from '../../services/mcp/types.js'
|
||||||
|
|
||||||
const FAVORITE_PAGE_SIZE = 100
|
const FAVORITE_PAGE_SIZE = 20
|
||||||
const FAVORITE_MAX_PAGES = 20
|
const FAVORITE_MAX_PAGES = 20
|
||||||
|
const FAVORITE_LIST_CACHE_TTL_MS = 30000
|
||||||
|
|
||||||
|
let _listFavoriteItemsCache: {
|
||||||
|
promise: Promise<FavoriteItemWithStatus[]>
|
||||||
|
expiry: number
|
||||||
|
} | null = null
|
||||||
|
|
||||||
export type FavoriteItemType = 'skill' | 'agent' | 'command' | 'mcp'
|
export type FavoriteItemType = 'skill' | 'agent' | 'command' | 'mcp'
|
||||||
|
|
||||||
|
|
@ -395,8 +401,19 @@ async function ensureInstalled(slugOrId: string): Promise<FavoriteStateRecord> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const SUPPORTED_MCP_TYPES = [
|
||||||
|
'stdio',
|
||||||
|
'sse',
|
||||||
|
'http',
|
||||||
|
'ws',
|
||||||
|
'sdk',
|
||||||
|
'claudeai-proxy',
|
||||||
|
'sse-ide',
|
||||||
|
'ws-ide',
|
||||||
|
]
|
||||||
|
|
||||||
function convertMcpConfig(config: Record<string, unknown>): Record<string, unknown> | undefined {
|
function convertMcpConfig(config: Record<string, unknown>): Record<string, unknown> | undefined {
|
||||||
if (typeof config.type === 'string' && (config.type === 'local' || config.type === 'remote')) {
|
if (typeof config.type === 'string' && SUPPORTED_MCP_TYPES.includes(config.type)) {
|
||||||
return config
|
return config
|
||||||
}
|
}
|
||||||
if (config.mcpServers && typeof config.mcpServers === 'object' && !Array.isArray(config.mcpServers)) {
|
if (config.mcpServers && typeof config.mcpServers === 'object' && !Array.isArray(config.mcpServers)) {
|
||||||
|
|
@ -430,11 +447,14 @@ function convertSingleMcpServer(server: Record<string, unknown>): Record<string,
|
||||||
}
|
}
|
||||||
if (cmdArray.length === 0) return undefined
|
if (cmdArray.length === 0) return undefined
|
||||||
const result: Record<string, unknown> = {
|
const result: Record<string, unknown> = {
|
||||||
type: 'local',
|
type: 'stdio',
|
||||||
command: cmdArray,
|
command: cmdArray[0],
|
||||||
|
}
|
||||||
|
if (cmdArray.length > 1) {
|
||||||
|
result.args = cmdArray.slice(1)
|
||||||
}
|
}
|
||||||
if (typeof server.environment === 'object' && server.environment !== null) {
|
if (typeof server.environment === 'object' && server.environment !== null) {
|
||||||
result.environment = server.environment
|
result.env = server.environment
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
@ -572,18 +592,29 @@ async function readItemForConfig(installed: FavoriteStateRecord): Promise<Favori
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function listFavoriteItems(type?: FavoriteItemType): Promise<FavoriteItemWithStatus[]> {
|
export async function listFavoriteItems(type?: FavoriteItemType): Promise<FavoriteItemWithStatus[]> {
|
||||||
|
const cacheKey = type ?? 'all'
|
||||||
|
const now = Date.now()
|
||||||
|
if (
|
||||||
|
_listFavoriteItemsCache &&
|
||||||
|
_listFavoriteItemsCache.expiry > now &&
|
||||||
|
cacheKey === 'all'
|
||||||
|
) {
|
||||||
|
return _listFavoriteItemsCache.promise
|
||||||
|
}
|
||||||
|
|
||||||
const storeTypes = type ? [LOCAL_TO_STORE_TYPE[type]] : Object.values(LOCAL_TO_STORE_TYPE)
|
const storeTypes = type ? [LOCAL_TO_STORE_TYPE[type]] : Object.values(LOCAL_TO_STORE_TYPE)
|
||||||
const errors: Error[] = []
|
const errors: Error[] = []
|
||||||
const candidatePages = await Promise.all(
|
const candidates: Array<{ id: string } & Record<string, unknown>> = []
|
||||||
[...new Set(storeTypes)].map(async (st) =>
|
|
||||||
listRemoteCandidates(st, { favorited: 'true' }).catch((error) => {
|
for (const st of [...new Set(storeTypes)]) {
|
||||||
console.warn('failed to fetch remote favorite candidates', { type: st, error })
|
try {
|
||||||
if (error instanceof Error) errors.push(error)
|
const page = await listRemoteCandidates(st, { favorited: 'true' })
|
||||||
return []
|
candidates.push(...page)
|
||||||
}),
|
} catch (error) {
|
||||||
),
|
console.warn('failed to fetch remote favorite candidates', { type: st, error })
|
||||||
)
|
if (error instanceof Error) errors.push(error)
|
||||||
const candidates = candidatePages.flat()
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// If all remote fetches failed, propagate the first error so the UI can show it
|
// If all remote fetches failed, propagate the first error so the UI can show it
|
||||||
if (candidates.length === 0 && errors.length > 0) {
|
if (candidates.length === 0 && errors.length > 0) {
|
||||||
|
|
@ -604,6 +635,7 @@ export async function listFavoriteItems(type?: FavoriteItemType): Promise<Favori
|
||||||
for (const candidate of candidates) {
|
for (const candidate of candidates) {
|
||||||
const item = parseFavoriteListItem(candidate)
|
const item = parseFavoriteListItem(candidate)
|
||||||
if (!item) continue
|
if (!item) continue
|
||||||
|
if (!item.favorited) continue
|
||||||
if (seen.has(item.slug)) continue
|
if (seen.has(item.slug)) continue
|
||||||
seen.add(item.slug)
|
seen.add(item.slug)
|
||||||
const local = state.items[item.slug]
|
const local = state.items[item.slug]
|
||||||
|
|
@ -632,7 +664,16 @@ export async function listFavoriteItems(type?: FavoriteItemType): Promise<Favori
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return result.sort((a, b) => a.name.localeCompare(b.name))
|
const sorted = result.sort((a, b) => a.name.localeCompare(b.name))
|
||||||
|
|
||||||
|
if (!type) {
|
||||||
|
_listFavoriteItemsCache = {
|
||||||
|
promise: Promise.resolve(sorted),
|
||||||
|
expiry: Date.now() + FAVORITE_LIST_CACHE_TTL_MS,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return sorted
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function viewFavoriteItem(slugOrId: string): Promise<FavoriteItemWithStatus> {
|
export async function viewFavoriteItem(slugOrId: string): Promise<FavoriteItemWithStatus> {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user