feat: add Hermes frontend types, API layer, and hooks (Phase 7)

- Add "hermes" to AppId union type and all exhaustive Record<AppId>
- Add HermesModelConfig, HermesAgentConfig, HermesEnvConfig types
- Add hermes field to VisibleApps, McpApps, ProxyTakeoverStatus
- Create src/lib/api/hermes.ts with Tauri invoke wrappers
- Create src/hooks/useHermes.ts with 5 query + 3 mutation hooks
- Register hermes in APP_IDS, APP_ICON_MAP (violet color scheme)
- Split MCP_SKILLS_APP_IDS into MCP_APP_IDS (includes hermes) and
  SKILLS_APP_IDS (excludes hermes, since Hermes has no Skills support)
- Wire hermes additive-mode into App.tsx (remove/duplicate handlers),
  ProviderList.tsx (live provider ID query + In Config badge),
  mutations.ts (cache invalidation on switch/add/delete)
- Add Hermes checkbox to McpFormModal
- Add basic hermes i18n keys (en/zh/ja)
This commit is contained in:
Jason
2026-04-15 17:41:33 +08:00
parent 576ff53a75
commit a0b585992a
23 changed files with 467 additions and 33 deletions
+18
View File
@@ -67,6 +67,7 @@ const McpFormModal: React.FC<McpFormModalProps> = ({
gemini: boolean;
opencode: boolean;
openclaw: boolean;
hermes: boolean;
}>(() => {
if (initialData?.apps) {
return { ...initialData.apps };
@@ -77,6 +78,7 @@ const McpFormModal: React.FC<McpFormModalProps> = ({
gemini: defaultEnabledApps.includes("gemini"),
opencode: defaultEnabledApps.includes("opencode"),
openclaw: defaultEnabledApps.includes("openclaw"),
hermes: defaultEnabledApps.includes("hermes"),
};
});
@@ -579,6 +581,22 @@ const McpFormModal: React.FC<McpFormModalProps> = ({
{t("mcp.unifiedPanel.apps.opencode")}
</label>
</div>
<div className="flex items-center gap-2">
<Checkbox
id="enable-hermes"
checked={enabledApps.hermes}
onCheckedChange={(checked: boolean) =>
setEnabledApps({ ...enabledApps, hermes: checked })
}
/>
<label
htmlFor="enable-hermes"
className="text-sm text-foreground cursor-pointer select-none"
>
{t("mcp.unifiedPanel.apps.hermes")}
</label>
</div>
</div>
</div>
+12 -5
View File
@@ -17,7 +17,7 @@ import { Edit3, Trash2, ExternalLink } from "lucide-react";
import { settingsApi } from "@/lib/api";
import { mcpPresets } from "@/config/mcpPresets";
import { toast } from "sonner";
import { MCP_SKILLS_APP_IDS } from "@/config/appConfig";
import { MCP_APP_IDS } from "@/config/appConfig";
import { AppCountBar } from "@/components/common/AppCountBar";
import { AppToggleGroup } from "@/components/common/AppToggleGroup";
import { ListItemRow } from "@/components/common/ListItemRow";
@@ -56,9 +56,16 @@ const UnifiedMcpPanel = React.forwardRef<
}, [serversMap]);
const enabledCounts = useMemo(() => {
const counts = { claude: 0, codex: 0, gemini: 0, opencode: 0, openclaw: 0 };
const counts = {
claude: 0,
codex: 0,
gemini: 0,
opencode: 0,
openclaw: 0,
hermes: 0,
};
serverEntries.forEach(([_, server]) => {
for (const app of MCP_SKILLS_APP_IDS) {
for (const app of MCP_APP_IDS) {
if (server.apps[app]) counts[app]++;
}
});
@@ -136,7 +143,7 @@ const UnifiedMcpPanel = React.forwardRef<
<AppCountBar
totalLabel={t("mcp.serverCount", { count: serverEntries.length })}
counts={enabledCounts}
appIds={MCP_SKILLS_APP_IDS}
appIds={MCP_APP_IDS}
/>
<div className="flex-1 overflow-y-auto overflow-x-hidden pb-24">
@@ -278,7 +285,7 @@ const UnifiedMcpListItem: React.FC<UnifiedMcpListItemProps> = ({
<AppToggleGroup
apps={server.apps}
onToggle={(app, enabled) => onToggleApp(id, app, enabled)}
appIds={MCP_SKILLS_APP_IDS}
appIds={MCP_APP_IDS}
/>
<div className="flex items-center gap-0.5 flex-shrink-0 opacity-0 group-hover:opacity-100 transition-opacity">