diff --git a/apps/cli/src/tui/cline-account.ts b/apps/cli/src/tui/cline-account.ts index 54d06ba492..4efdc003da 100644 --- a/apps/cli/src/tui/cline-account.ts +++ b/apps/cli/src/tui/cline-account.ts @@ -14,6 +14,8 @@ import { toProviderApiKey } from "../utils/provider-auth"; import type { Config } from "../utils/types"; const WORKOS_TOKEN_PREFIX = "workos:"; +export const CLINE_CREDITS_DASHBOARD_URL = + "https://app.cline.bot/dashboard/account?tab=credits"; type ClineAccountConfig = Pick; @@ -30,6 +32,8 @@ export function formatClineCredits(value: number): string { return formatCreditBalance(normalizeCreditBalance(value)); } +// FIXME: These message checks are temporary until structured error types are +// passed through to the CLI instead of plain error strings. export function isClineAccountAuthErrorMessage(message: string): boolean { const normalized = message.trim().toLowerCase(); return ( @@ -38,6 +42,14 @@ export function isClineAccountAuthErrorMessage(message: string): boolean { ); } +export function isClineAccountCreditsErrorMessage(message: string): boolean { + const normalized = message.trim().toLowerCase(); + return ( + normalized.includes("insufficient balance") && + normalized.includes("cline credits balance") + ); +} + function resolveAccountApiBaseUrl(input: { clineApiBaseUrl?: string; clineProviderSettings?: ProviderSettings; diff --git a/apps/cli/src/tui/components/chat-entry.tsx b/apps/cli/src/tui/components/chat-entry.tsx index dea10bf78d..880277a849 100644 --- a/apps/cli/src/tui/components/chat-entry.tsx +++ b/apps/cli/src/tui/components/chat-entry.tsx @@ -2,6 +2,10 @@ import { useTerminalDimensions } from "@opentui/react"; import type React from "react"; import { useState } from "react"; import "opentui-spinner/react"; +import { + CLINE_CREDITS_DASHBOARD_URL, + isClineAccountCreditsErrorMessage, +} from "../cline-account"; import { useTerminalBackground } from "../hooks/use-terminal-background"; import { getDefaultForeground, @@ -256,6 +260,36 @@ function ToolCallView(props: { ); } +function ClineCreditsErrorView(props: { defaultFg?: string }) { + return ( + + + + Cline Credits depleted + + + Dashboard: + + + {CLINE_CREDITS_DASHBOARD_URL} + + + + + + ); +} + export function ChatEntryView(props: { entry: ChatEntry; accent?: string; @@ -351,6 +385,9 @@ export function ChatEntryView(props: { ); case "error": + if (isClineAccountCreditsErrorMessage(entry.text)) { + return ; + } return (