feat(desktop): run tool backend post-setup installs from the GUI (#40559)

Complete the desktop app's tool-backend configuration so it fully
mirrors `hermes tools`. The toolset config panel already did
enable/disable, provider selection, and API-key save/reveal/clear — the
one remaining gap was post-setup install hooks, which previously just
told the user to run the CLI.

Now a provider that declares a post_setup hook (browser Chromium,
Camofox, cua-driver, KittenTTS/Piper, ddgs, Spotify, Langfuse, xAI)
renders a 'Run setup' button that spawns the install via the
`POST /api/tools/toolsets/{name}/post-setup` endpoint and tails the
log inline, feeding the desktop activity rail — mirroring
command-center's runSystemAction poll loop. On completion the panel
refreshes so a now-installed backend reports itself ready.

- hermes.ts: runToolsetPostSetup(name, key) -> profile-scoped POST.
- toolset-config-panel.tsx: PostSetupRunner sub-component (Run setup
  button + inline live log + activity-rail upsert + unmount guard),
  replacing the CLI-only placeholder.
- i18n: replace the orphaned `toolsets.postSetup` (CLI redirect) string
  with proper post-setup UI keys (hint / run / running / starting /
  complete / error / failed) across en, ja, zh, zh-hant + types.
- test: post-setup run+poll+log-tail coverage; mock additions for
  runToolsetPostSetup/getActionStatus/activity store.

Works against local AND remote backends: all calls route through the
desktop's single `hermes:api` IPC handler to connection.baseUrl, so a
connected remote configures the remote host's tools (keys -> remote
.env, install runs on the remote). Relies on the post-setup endpoint +
'hermes tools post-setup' CLI shipped in #40418.

Verification: tsc -b clean (all 5 locales), eslint clean (the lone
exhaustive-deps warning is pre-existing on origin/main), vitest 4/5
(new post-setup test passes; the failing 'saves an API key' test fails
identically on origin/main — pre-existing EnvVarActionsMenu drift).
This commit is contained in:
Teknium
2026-06-06 18:35:02 -07:00
committed by GitHub
parent 8f7567c325
commit 97524344ad
8 changed files with 323 additions and 15 deletions
+10 -2
View File
@@ -542,8 +542,16 @@ export const en: Translations = {
ready: 'Ready',
nousIncluded: 'Included with a Nous subscription — sign in to Nous Portal to activate.',
noApiKeyRequired: 'No API key required.',
postSetup: step =>
`This provider needs an extra setup step (${step}). Run it from the CLI with hermes tools for now.`
postSetupHint: step =>
`This backend needs a one-time install (${step}). Runs on this machine — may take a few minutes.`,
postSetupRun: 'Run setup',
postSetupRunning: 'Installing…',
postSetupStarting: 'Starting…',
postSetupCompleteTitle: 'Setup complete',
postSetupCompleteMessage: step => `${step} installed.`,
postSetupErrorTitle: 'Setup finished with errors',
postSetupErrorMessage: step => `Check the ${step} log.`,
postSetupFailed: step => `Failed to run ${step} setup`
}
},
+10 -2
View File
@@ -637,8 +637,16 @@ export const ja = defineLocale({
ready: '準備完了',
nousIncluded: 'Nous サブスクリプションに含まれています。有効にするには Nous Portal にサインインしてください。',
noApiKeyRequired: 'API キーは不要です。',
postSetup: step =>
`このプロバイダーは追加のセットアップ手順 (${step}) が必要です。今は CLI で hermes tools を実行してください`
postSetupHint: step =>
`このバックエンドは一度だけインストールが必要です (${step})。このマシン上で実行され、数分かかる場合があります`,
postSetupRun: 'セットアップを実行',
postSetupRunning: 'インストール中…',
postSetupStarting: '開始中…',
postSetupCompleteTitle: 'セットアップ完了',
postSetupCompleteMessage: step => `${step} をインストールしました。`,
postSetupErrorTitle: 'セットアップはエラーで終了しました',
postSetupErrorMessage: step => `${step} のログを確認してください。`,
postSetupFailed: step => `${step} のセットアップの実行に失敗しました`
}
},
+9 -1
View File
@@ -450,7 +450,15 @@ export interface Translations {
ready: string
nousIncluded: string
noApiKeyRequired: string
postSetup: (step: string) => string
postSetupHint: (step: string) => string
postSetupRun: string
postSetupRunning: string
postSetupStarting: string
postSetupCompleteTitle: string
postSetupCompleteMessage: (step: string) => string
postSetupErrorTitle: string
postSetupErrorMessage: (step: string) => string
postSetupFailed: (step: string) => string
}
}
+9 -1
View File
@@ -621,7 +621,15 @@ export const zhHant = defineLocale({
ready: '就緒',
nousIncluded: '包含在 Nous 訂閱中;登入 Nous Portal 即可啟用。',
noApiKeyRequired: '不需要 API 金鑰。',
postSetup: step => `提供方需要額外設定步驟 (${step})。暫時請在 CLI 中執行 hermes tools`
postSetupHint: step => `後端需要一次性安裝 (${step})。將在此機器上執行,可能需要幾分鐘`,
postSetupRun: '執行設定',
postSetupRunning: '安裝中…',
postSetupStarting: '啟動中…',
postSetupCompleteTitle: '設定完成',
postSetupCompleteMessage: step => `已安裝 ${step}`,
postSetupErrorTitle: '設定完成但有錯誤',
postSetupErrorMessage: step => `請檢查 ${step} 日誌。`,
postSetupFailed: step => `執行 ${step} 設定失敗`
}
},
+9 -1
View File
@@ -696,7 +696,15 @@ export const zh: Translations = {
ready: '就绪',
nousIncluded: '包含在 Nous 订阅中;登录 Nous Portal 即可激活。',
noApiKeyRequired: '不需要 API 密钥。',
postSetup: step => `提供方需要额外设置步骤 (${step})。暂时请在 CLI 中运行 hermes tools`
postSetupHint: step => `后端需要一次性安装 (${step})。将在此机器上执行,可能需要几分钟`,
postSetupRun: '运行设置',
postSetupRunning: '安装中…',
postSetupStarting: '启动中…',
postSetupCompleteTitle: '设置完成',
postSetupCompleteMessage: step => `已安装 ${step}`,
postSetupErrorTitle: '设置完成但有错误',
postSetupErrorMessage: step => `请检查 ${step} 日志。`,
postSetupFailed: step => `运行 ${step} 设置失败`
}
},