Hotfix-cant-run-tools-when-no-credits (#5955)

Tested:
* Can sign in on saas -> can run local tools with or without credits->
can run saas only tools (if credits) -> can't run saas only tools
without credits
* Can sign in self-hosted -> can run all tools on remote if available ->
can run local when self-hosted unavailable

Clouds show on saas tools when connected
Tools are disabled when connected to self-hosted but cannot find server.
You also get banner


#cantwaitforplaywritetests
This commit is contained in:
ConnorYoh
2026-03-17 13:01:08 +00:00
committed by GitHub
parent b656e1e2d1
commit 214dc20c2e
8 changed files with 61 additions and 29 deletions
@@ -100,14 +100,18 @@ export function useConversionCloudStatus(): ConversionStatus {
pairs.map(async ([fromExt, toExt, endpointName]) => {
const key = `${fromExt}-${toExt}`;
try {
const combined = await endpointAvailabilityService.checkEndpointCombined(
// In SaaS mode, everything is available (locally or via cloud routing).
// Only check local support to determine willUseCloud — the same approach
// used by useMultipleEndpointsEnabled's SaaS enhancement.
const availableLocally = await endpointAvailabilityService.isEndpointSupportedLocally(
endpointName,
tauriBackendService.getBackendUrl()
);
return { key, isAvailable: combined.isAvailable, willUseCloud: combined.willUseCloud, localOnly: combined.localOnly };
return { key, isAvailable: true, willUseCloud: !availableLocally, localOnly: false };
} catch (error) {
console.error(`[useConversionCloudStatus] Endpoint check failed for ${key}:`, error);
return { key, isAvailable: false, willUseCloud: false, localOnly: false };
// On error, assume available via cloud (safe default in SaaS mode)
return { key, isAvailable: true, willUseCloud: true, localOnly: false };
}
})
);
+16 -3
View File
@@ -4,6 +4,7 @@ import { useSaaSBilling } from '@app/contexts/SaasBillingContext';
import { useSaaSMode } from '@app/hooks/useSaaSMode';
import { getToolCreditCost } from '@app/utils/creditCosts';
import { CREDIT_EVENTS } from '@app/constants/creditEvents';
import { operationRouter } from '@app/services/operationRouter';
import type { ToolId } from '@app/types/toolId';
/**
@@ -15,15 +16,27 @@ import type { ToolId } from '@app/types/toolId';
* Returns null when the operation is allowed, or an error message string
* when it should be blocked.
*/
export function useCreditCheck(operationType?: string) {
export function useCreditCheck(operationType?: string, endpoint?: string) {
const billing = useSaaSBilling();
const isSaaSMode = useSaaSMode();
const { t } = useTranslation();
const checkCredits = useCallback(async (): Promise<string | null> => {
const checkCredits = useCallback(async (runtimeEndpoint?: string): Promise<string | null> => {
if (!isSaaSMode) return null; // Credits only apply in SaaS mode, not self-hosted
if (!billing) return null;
// If the operation routes to the local backend, no credits are consumed — skip check.
// runtimeEndpoint (from params at execution time) takes priority over hook-level endpoint.
const ep = runtimeEndpoint ?? endpoint;
if (ep) {
try {
const willUseSaaS = await operationRouter.willRouteToSaaS(ep);
if (!willUseSaaS) return null;
} catch {
// If routing check fails, fall through to credit check as a safe default.
}
}
const { creditBalance, loading } = billing;
const requiredCredits = getToolCreditCost(operationType as ToolId);
@@ -44,7 +57,7 @@ export function useCreditCheck(operationType?: string) {
}
return null;
}, [billing, isSaaSMode, operationType, t]);
}, [billing, isSaaSMode, operationType, endpoint, t]);
return { checkCredits };
}