diff --git a/apps/presentation/dashboard/src/features/personal-workspace/lark-settings-page.tsx b/apps/presentation/dashboard/src/features/personal-workspace/lark-settings-page.tsx index a461ffb1d3..f4fe5d7588 100644 --- a/apps/presentation/dashboard/src/features/personal-workspace/lark-settings-page.tsx +++ b/apps/presentation/dashboard/src/features/personal-workspace/lark-settings-page.tsx @@ -153,6 +153,14 @@ function larkGroupHistoryPermissionUrl(connection: LarkGoalConnection): string | return connection.history_permission_guidance?.api_document_url ?? null; } +// lark-cli is resolved once when the Chat service starts, so these codes stay +// true until the operator installs it and restarts LoopX. +const larkCliUnavailableCodes = new Set(["lark_cli_not_installed", "lark_cli_not_executable"]); + +function larkCliUnavailable(cause: unknown): boolean { + return cause instanceof ChatApiError && larkCliUnavailableCodes.has(String(cause.payload.error_code ?? "")); +} + function larkErrorMessage(cause: unknown, fallback: string, t: WorkspaceTranslate): string { if (cause instanceof ChatApiError) { const code = String(cause.payload.error_code ?? ""); @@ -219,6 +227,7 @@ export function LarkSettingsPage({ const [setupBrand, setSetupBrand] = useState<"feishu" | "lark">("feishu"); const [setupSnapshot, setSetupSnapshot] = useState(null); const [setupStarting, setSetupStarting] = useState(false); + const [cliUnavailable, setCliUnavailable] = useState(false); const [setupError, setSetupError] = useState(null); const setupPopup = useRef(null); const openedSetupUrl = useRef(null); @@ -234,8 +243,10 @@ export function LarkSettingsPage({ ]); setApps(nextApps); setConnections(nextConnections); + setCliUnavailable(false); setAppRef((current) => current || nextApps.find((app) => app.reply_ready)?.app_ref || nextApps.find((app) => app.ready)?.app_ref || nextApps[0]?.app_ref || ""); } catch (cause) { + setCliUnavailable(larkCliUnavailable(cause)); setError(larkErrorMessage(cause, t("lark.error.configuration"), t)); } finally { setLoading(false); @@ -405,6 +416,9 @@ export function LarkSettingsPage({ } function openSetup() { + // Every setup entry (toolbar, connection dialog) meets the same fact: + // without lark-cli the setup can only fail until LoopX restarts. + if (cliUnavailable) return; setSetupSnapshot(null); setSetupError(null); openedSetupUrl.current = null; @@ -517,7 +531,7 @@ export function LarkSettingsPage({ {!loading && tab === "apps" ? (
-
{t("lark.reusableApps", { count: apps.length })}
+
{t("lark.reusableApps", { count: apps.length })}
{apps.map((app) => (
@@ -583,7 +597,7 @@ export function LarkSettingsPage({ {t("lark.editPreservesIdentity")} : <> - + {selectedApp?.ready && !selectedApp.reply_ready ?
{t("lark.appPermissions")}
: null}