mirror of
https://github.com/Nezumi-2711/9router.git
synced 2026-09-22 20:00:47 +00:00
Feat : Skills
This commit is contained in:
@@ -123,6 +123,13 @@ const getPageInfo = (pathname) => {
|
||||
icon: "lan",
|
||||
breadcrumbs: [],
|
||||
};
|
||||
if (pathname.includes("/skills"))
|
||||
return {
|
||||
title: "Agent Skills",
|
||||
description: "Copy a link and paste to your AI to use 9Router — no install needed",
|
||||
icon: "extension",
|
||||
breadcrumbs: [],
|
||||
};
|
||||
if (pathname.includes("/endpoint"))
|
||||
return {
|
||||
title: "Endpoint",
|
||||
|
||||
@@ -34,6 +34,7 @@ const debugItems = [
|
||||
|
||||
const systemItems = [
|
||||
{ href: "/dashboard/proxy-pools", label: "Proxy Pools", icon: "lan" },
|
||||
{ href: "/dashboard/skills", label: "Skills", icon: "extension" },
|
||||
];
|
||||
|
||||
export default function Sidebar({ onClose }) {
|
||||
@@ -171,14 +172,14 @@ export default function Sidebar({ onClose }) {
|
||||
</div>
|
||||
|
||||
{/* Navigation */}
|
||||
<nav className="flex-1 px-4 py-2 space-y-1 overflow-y-auto custom-scrollbar">
|
||||
<nav className="flex-1 px-4 py-2 space-y-0.5 overflow-y-auto custom-scrollbar">
|
||||
{navItems.map((item) => (
|
||||
<Link
|
||||
key={item.href}
|
||||
href={item.href}
|
||||
onClick={onClose}
|
||||
className={cn(
|
||||
"flex items-center gap-3 px-3 py-1.5 rounded-lg transition-all group",
|
||||
"flex items-center gap-3 px-3 py-1 rounded-lg transition-all group",
|
||||
isActive(item.href)
|
||||
? "bg-primary/10 text-primary"
|
||||
: "text-text-muted hover:bg-surface-2 hover:text-text-main"
|
||||
@@ -197,7 +198,7 @@ export default function Sidebar({ onClose }) {
|
||||
))}
|
||||
|
||||
{/* System section */}
|
||||
<div className="pt-4 mt-2 space-y-1">
|
||||
<div className="pt-3 mt-2 space-y-0.5">
|
||||
<p className="px-4 text-xs font-semibold text-text-muted/60 uppercase tracking-wider mb-2">
|
||||
System
|
||||
</p>
|
||||
@@ -206,7 +207,7 @@ export default function Sidebar({ onClose }) {
|
||||
<button
|
||||
onClick={() => setMediaOpen((v) => !v)}
|
||||
className={cn(
|
||||
"w-full flex items-center gap-3 px-3 py-1.5 rounded-lg transition-all group",
|
||||
"w-full flex items-center gap-3 px-3 py-1 rounded-lg transition-all group",
|
||||
pathname.startsWith("/dashboard/media-providers")
|
||||
? "bg-primary/10 text-primary"
|
||||
: "text-text-muted hover:bg-surface-2 hover:text-text-main"
|
||||
@@ -226,7 +227,7 @@ export default function Sidebar({ onClose }) {
|
||||
href={`/dashboard/media-providers/${kind.id}`}
|
||||
onClick={onClose}
|
||||
className={cn(
|
||||
"flex items-center gap-3 px-4 py-1.5 rounded-lg transition-all group",
|
||||
"flex items-center gap-3 px-4 py-1 rounded-lg transition-all group",
|
||||
pathname.startsWith(`/dashboard/media-providers/${kind.id}`)
|
||||
? "bg-primary/10 text-primary"
|
||||
: "text-text-muted hover:bg-surface-2 hover:text-text-main"
|
||||
@@ -241,7 +242,7 @@ export default function Sidebar({ onClose }) {
|
||||
href={COMBINED_WEB_ITEM.href}
|
||||
onClick={onClose}
|
||||
className={cn(
|
||||
"flex items-center gap-3 px-4 py-1.5 rounded-lg transition-all group",
|
||||
"flex items-center gap-3 px-4 py-1 rounded-lg transition-all group",
|
||||
pathname.startsWith(COMBINED_WEB_ITEM.href)
|
||||
? "bg-primary/10 text-primary"
|
||||
: "text-text-muted hover:bg-surface-2 hover:text-text-main"
|
||||
@@ -259,7 +260,7 @@ export default function Sidebar({ onClose }) {
|
||||
href={item.href}
|
||||
onClick={onClose}
|
||||
className={cn(
|
||||
"flex items-center gap-3 px-3 py-1.5 rounded-lg transition-all group",
|
||||
"flex items-center gap-3 px-3 py-1 rounded-lg transition-all group",
|
||||
isActive(item.href)
|
||||
? "bg-primary/10 text-primary"
|
||||
: "text-text-muted hover:bg-surface-2 hover:text-text-main"
|
||||
@@ -286,7 +287,7 @@ export default function Sidebar({ onClose }) {
|
||||
href={item.href}
|
||||
onClick={onClose}
|
||||
className={cn(
|
||||
"flex items-center gap-3 px-3 py-1.5 rounded-lg transition-all group",
|
||||
"flex items-center gap-3 px-3 py-1 rounded-lg transition-all group",
|
||||
isActive(item.href)
|
||||
? "bg-primary/10 text-primary"
|
||||
: "text-text-muted hover:bg-surface-2 hover:text-text-main"
|
||||
@@ -310,7 +311,7 @@ export default function Sidebar({ onClose }) {
|
||||
href="/dashboard/profile"
|
||||
onClick={onClose}
|
||||
className={cn(
|
||||
"flex items-center gap-3 px-3 py-1.5 rounded-lg transition-all group",
|
||||
"flex items-center gap-3 px-3 py-1 rounded-lg transition-all group",
|
||||
isActive("/dashboard/profile")
|
||||
? "bg-primary/10 text-primary"
|
||||
: "text-text-muted hover:bg-surface-2 hover:text-text-main"
|
||||
|
||||
@@ -82,7 +82,7 @@ export const APIKEY_PROVIDERS = {
|
||||
hyperbolic: { id: "hyperbolic", alias: "hyp", name: "Hyperbolic", icon: "bolt", color: "#00D4FF", textIcon: "HY", website: "https://hyperbolic.xyz", notice: { apiKeyUrl: "https://app.hyperbolic.xyz/settings" }, serviceKinds: ["llm", "tts"], ttsConfig: { baseUrl: "https://api.hyperbolic.xyz/v1/audio/generation", authType: "apikey", authHeader: "bearer", format: "hyperbolic", models: [{ id: "melo-tts", name: "Melo TTS" }] } },
|
||||
deepgram: { id: "deepgram", alias: "dg", name: "Deepgram", icon: "mic", color: "#13EF93", textIcon: "DG", website: "https://deepgram.com", notice: { text: "$200 free credit on signup (no card required). Aura-1: $0.015/1k chars, Aura-2: $0.030/1k chars (Pay-As-You-Go).", apiKeyUrl: "https://console.deepgram.com/api-keys" }, serviceKinds: ["stt", "imageToText", "tts"], ttsConfig: { baseUrl: "https://api.deepgram.com/v1/speak", authType: "apikey", authHeader: "token", format: "deepgram", models: [] } },
|
||||
assemblyai: { id: "assemblyai", alias: "aai", name: "AssemblyAI", icon: "record_voice_over", color: "#0062FF", textIcon: "AA", website: "https://assemblyai.com", notice: { apiKeyUrl: "https://www.assemblyai.com/app/api-keys" }, serviceKinds: ["stt"] },
|
||||
nanobanana: { id: "nanobanana", alias: "nb", name: "NanoBanana", icon: "image", color: "#FFD700", textIcon: "NB", website: "https://nanobananaapi.ai", notice: { apiKeyUrl: "https://nanobananaapi.ai/dashboard" }, serviceKinds: ["image"] },
|
||||
nanobanana: { id: "nanobanana", alias: "nb", name: "NanoBanana API", icon: "extension", color: "#FFD700", textIcon: "🍌", website: "https://nanobananaapi.ai", notice: { text: "3rd-party proxy for Google Nano Banana (Gemini 2.5/3 Flash Image). For official, use Gemini provider.", apiKeyUrl: "https://nanobananaapi.ai/dashboard" }, serviceKinds: ["image"] },
|
||||
elevenlabs: { id: "elevenlabs", alias: "el", name: "ElevenLabs", icon: "record_voice_over", color: "#6C47FF", textIcon: "EL", website: "https://elevenlabs.io", notice: { apiKeyUrl: "https://elevenlabs.io/app/settings/api-keys" }, serviceKinds: ["tts"], ttsConfig: { baseUrl: "https://api.elevenlabs.io/v1/text-to-speech", authType: "apikey", authHeader: "xi-api-key", format: "elevenlabs", models: [{ id: "eleven_multilingual_v2", name: "Eleven Multilingual v2" }, { id: "eleven_turbo_v2_5", name: "Eleven Turbo v2.5" }] } },
|
||||
cartesia: { id: "cartesia", alias: "cartesia", name: "Cartesia", icon: "spatial_audio", color: "#FF4F8B", textIcon: "CA", website: "https://cartesia.ai", notice: { apiKeyUrl: "https://play.cartesia.ai/keys" }, serviceKinds: ["tts"], hidden: true, ttsConfig: { baseUrl: "https://api.cartesia.ai/tts/bytes", authType: "apikey", authHeader: "x-api-key", format: "cartesia", models: [{ id: "sonic-2", name: "Sonic 2" }, { id: "sonic-3", name: "Sonic 3" }] } },
|
||||
playht: { id: "playht", alias: "playht", name: "PlayHT", icon: "play_circle", color: "#00B4D8", textIcon: "PH", website: "https://play.ht", notice: { apiKeyUrl: "https://play.ht/studio/api-access" }, serviceKinds: ["tts"], hidden: true, ttsConfig: { baseUrl: "https://api.play.ht/api/v2/tts/stream", authType: "apikey", authHeader: "playht", format: "playht", models: [{ id: "PlayDialog", name: "PlayDialog" }, { id: "Play3.0-mini", name: "Play 3.0 Mini" }] } },
|
||||
@@ -110,6 +110,14 @@ export const APIKEY_PROVIDERS = {
|
||||
searchapi: { id: "searchapi", alias: "searchapi", name: "SearchAPI", icon: "search", color: "#0EA5A4", textIcon: "SA", website: "https://www.searchapi.io", notice: { apiKeyUrl: "https://www.searchapi.io/dashboard" }, serviceKinds: ["webSearch"], searchConfig: { baseUrl: "https://www.searchapi.io/api/v1/search", method: "GET", authType: "apikey", authHeader: "api_key", costPerQuery: 0.004, freeMonthlyQuota: 100, searchTypes: ["web", "news"], defaultMaxResults: 5, maxMaxResults: 100, timeoutMs: 10000, cacheTTLMs: 300000 } },
|
||||
youcom: { id: "youcom", alias: "youcom", name: "You.com Search", icon: "search", color: "#7C3AED", textIcon: "YC", website: "https://you.com", notice: { apiKeyUrl: "https://api.you.com" }, serviceKinds: ["webSearch"], searchConfig: { baseUrl: "https://ydc-index.io/v1/search", method: "GET", authType: "apikey", authHeader: "x-api-key", costPerQuery: 0.005, freeMonthlyQuota: 0, searchTypes: ["web", "news"], defaultMaxResults: 5, maxMaxResults: 100, timeoutMs: 10000, cacheTTLMs: 300000 } },
|
||||
firecrawl: { id: "firecrawl", alias: "firecrawl", name: "Firecrawl", icon: "local_fire_department", color: "#F59E0B", textIcon: "FC", website: "https://firecrawl.dev", notice: { apiKeyUrl: "https://www.firecrawl.dev/app/api-keys" }, serviceKinds: ["webFetch"], fetchConfig: { baseUrl: "https://api.firecrawl.dev/v1/scrape", method: "POST", authType: "apikey", authHeader: "bearer", costPerQuery: 0.002, freeMonthlyQuota: 500, formats: ["markdown", "html", "text"], maxCharacters: 200000, timeoutMs: 30000 } },
|
||||
"fal-ai": { id: "fal-ai", alias: "fal", name: "Fal.ai", icon: "image", color: "#2563EB", textIcon: "FL", website: "https://fal.ai", notice: { apiKeyUrl: "https://fal.ai/dashboard/keys" }, serviceKinds: ["image"], imageConfig: { baseUrl: "https://queue.fal.run/fal-ai/flux/schnell", method: "POST", authType: "apikey", authHeader: "key" } },
|
||||
"stability-ai": { id: "stability-ai", alias: "stability", name: "Stability AI", icon: "image", color: "#8B5CF6", textIcon: "SA", website: "https://stability.ai", notice: { apiKeyUrl: "https://platform.stability.ai/account/keys" }, serviceKinds: ["image"], imageConfig: { baseUrl: "https://api.stability.ai/v1/user/account", method: "GET", authType: "apikey", authHeader: "bearer" } },
|
||||
"black-forest-labs": { id: "black-forest-labs", alias: "bfl", name: "Black Forest Labs", icon: "image", color: "#111827", textIcon: "BF", website: "https://blackforestlabs.ai", notice: { apiKeyUrl: "https://api.bfl.ai" }, serviceKinds: ["image"], imageConfig: { baseUrl: "https://api.bfl.ai/v1/get_result?id=ping", method: "GET", authType: "apikey", authHeader: "x-key" } },
|
||||
recraft: { id: "recraft", alias: "recraft", name: "Recraft", icon: "image", color: "#EC4899", textIcon: "RC", website: "https://recraft.ai", notice: { apiKeyUrl: "https://www.recraft.ai/profile/api" }, serviceKinds: ["image"], imageConfig: { baseUrl: "https://external.api.recraft.ai/v1/users/me", method: "GET", authType: "apikey", authHeader: "bearer" } },
|
||||
topaz: { id: "topaz", alias: "topaz", name: "Topaz", icon: "image", color: "#059669", textIcon: "TP", website: "https://topazlabs.com", notice: { apiKeyUrl: "https://topazlabs.com/account" }, serviceKinds: ["image"] },
|
||||
runwayml: { id: "runwayml", alias: "runway", name: "Runway ML", icon: "movie", color: "#000000", textIcon: "RW", website: "https://runwayml.com", notice: { apiKeyUrl: "https://dev.runwayml.com" }, serviceKinds: ["image", "video"], imageConfig: { baseUrl: "https://api.dev.runwayml.com/v1/organization", method: "GET", authType: "apikey", authHeader: "bearer", extraHeaders: { "X-Runway-Version": "2024-11-06" } } },
|
||||
"aws-polly": { id: "aws-polly", alias: "polly", name: "AWS Polly", icon: "record_voice_over", color: "#FF9900", textIcon: "PL", website: "https://aws.amazon.com/polly/", notice: { text: "Use AWS Secret Access Key as API key; set providerSpecificData.accessKeyId and optional region.", apiKeyUrl: "https://console.aws.amazon.com/iam/home#/security_credentials" }, serviceKinds: ["tts"], hasProviderSpecificData: true, ttsConfig: { baseUrl: "https://polly.{region}.amazonaws.com/v1/speech", authType: "apikey", authHeader: "aws-sigv4", format: "aws-polly", models: [{ id: "standard", name: "Standard" }, { id: "neural", name: "Neural" }, { id: "long-form", name: "Long-form" }, { id: "generative", name: "Generative" }] } },
|
||||
"jina-ai": { id: "jina-ai", alias: "jina", name: "Jina AI", icon: "blur_on", color: "#2563EB", textIcon: "JA", website: "https://jina.ai", notice: { text: "10M free tokens on signup (non-commercial), no credit card required.", apiKeyUrl: "https://jina.ai/?sui=apikey" }, serviceKinds: ["embedding"], embeddingConfig: { baseUrl: "https://api.jina.ai/v1/embeddings", authType: "apikey", authHeader: "bearer", models: [{ id: "jina-embeddings-v3", name: "Jina Embeddings v3", dimensions: 1024 }, { id: "jina-embeddings-v2-base-en", name: "Jina Embeddings v2 Base EN", dimensions: 768 }, { id: "jina-embeddings-v2-base-code", name: "Jina Embeddings v2 Base Code", dimensions: 768 }] } },
|
||||
"jina-reader": { id: "jina-reader", alias: "jina", name: "Jina Reader", icon: "menu_book", color: "#000000", textIcon: "JR", website: "https://jina.ai/reader", notice: { apiKeyUrl: "https://jina.ai/?sui=apikey" }, serviceKinds: ["webFetch"], fetchConfig: { baseUrl: "https://r.jina.ai", method: "GET", authType: "apikey", authHeader: "bearer", costPerQuery: 0, freeMonthlyQuota: 1000000, formats: ["markdown", "text", "html"], maxCharacters: 200000, timeoutMs: 30000 } },
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
// Agent Skills metadata — single source of truth for /dashboard/skills page.
|
||||
// Each skill = 1 raw GitHub URL the user copies and pastes to any AI agent.
|
||||
|
||||
const REPO = "decolua/9router";
|
||||
const BRANCH = "main";
|
||||
const SKILL_PATH = "app/skills";
|
||||
|
||||
export const SKILLS_REPO_URL = `https://github.com/${REPO}`;
|
||||
export const SKILLS_RAW_BASE = `https://raw.githubusercontent.com/${REPO}/${BRANCH}/${SKILL_PATH}`;
|
||||
export const SKILLS_BLOB_BASE = `https://github.com/${REPO}/blob/${BRANCH}/${SKILL_PATH}`;
|
||||
|
||||
export const SKILLS = [
|
||||
{
|
||||
id: "9router",
|
||||
name: "9Router (Entry)",
|
||||
description: "Setup + index of all capabilities. Start here — covers base URL, auth, model discovery, and links to every capability skill.",
|
||||
endpoint: null,
|
||||
icon: "hub",
|
||||
isEntry: true,
|
||||
},
|
||||
{
|
||||
id: "9router-chat",
|
||||
name: "Chat",
|
||||
description: "Chat / code-gen via OpenAI or Anthropic format with streaming.",
|
||||
endpoint: "/v1/chat/completions",
|
||||
icon: "chat",
|
||||
},
|
||||
{
|
||||
id: "9router-image",
|
||||
name: "Image Generation",
|
||||
description: "Text-to-image via DALL-E, Imagen, FLUX, MiniMax, SDWebUI…",
|
||||
endpoint: "/v1/images/generations",
|
||||
icon: "image",
|
||||
},
|
||||
{
|
||||
id: "9router-tts",
|
||||
name: "Text-to-Speech",
|
||||
description: "OpenAI / ElevenLabs / Edge / Google / Deepgram voices.",
|
||||
endpoint: "/v1/audio/speech",
|
||||
icon: "record_voice_over",
|
||||
},
|
||||
{
|
||||
id: "9router-embeddings",
|
||||
name: "Embeddings",
|
||||
description: "Vectors for RAG / semantic search via OpenAI, Gemini, Mistral…",
|
||||
endpoint: "/v1/embeddings",
|
||||
icon: "scatter_plot",
|
||||
},
|
||||
{
|
||||
id: "9router-web-search",
|
||||
name: "Web Search",
|
||||
description: "Tavily / Exa / Brave / Serper / SearXNG / Google PSE / You.com.",
|
||||
endpoint: "/v1/search",
|
||||
icon: "search",
|
||||
},
|
||||
{
|
||||
id: "9router-web-fetch",
|
||||
name: "Web Fetch",
|
||||
description: "URL → markdown / text / HTML via Firecrawl, Jina, Tavily, Exa.",
|
||||
endpoint: "/v1/web/fetch",
|
||||
icon: "language",
|
||||
},
|
||||
];
|
||||
|
||||
export function getSkillRawUrl(id) {
|
||||
return `${SKILLS_RAW_BASE}/${id}/SKILL.md`;
|
||||
}
|
||||
|
||||
export function getSkillBlobUrl(id) {
|
||||
return `${SKILLS_BLOB_BASE}/${id}/SKILL.md`;
|
||||
}
|
||||
Reference in New Issue
Block a user