fix(cli): show Cline credits refill link (#11345)

* fix(cli): show Cline credits refill link

* fix(cli): simplify Cline credits error matcher

* fix(cli): keep credits handling in TUI

* fix(cli): rename credits error matcher

* fix(cli): render credits dashboard as link

* fix(cli): remove credits redirect param

* fix(cli): document temporary credits matcher
This commit is contained in:
Saoud Rizwan
2026-06-07 19:58:34 -07:00
committed by GitHub
parent 7e2583f40c
commit ae67ca7a13
2 changed files with 49 additions and 0 deletions
+12
View File
@@ -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<Config, "apiKey" | "providerId">;
@@ -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;
@@ -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 (
<box flexDirection="row">
<text fg="red" content="* " />
<box
flexDirection="column"
border
borderStyle="rounded"
borderColor="red"
paddingX={1}
>
<text fg="red">Cline Credits depleted</text>
<text
fg={props.defaultFg}
selectable
content="You have run out of Cline credits. Add credits in the dashboard to continue."
/>
<box flexDirection="row">
<text fg="gray">Dashboard: </text>
<text fg="cyan" selectable>
<a href={CLINE_CREDITS_DASHBOARD_URL}>
{CLINE_CREDITS_DASHBOARD_URL}
</a>
</text>
</box>
</box>
</box>
);
}
export function ChatEntryView(props: {
entry: ChatEntry;
accent?: string;
@@ -351,6 +385,9 @@ export function ChatEntryView(props: {
);
case "error":
if (isClineAccountCreditsErrorMessage(entry.text)) {
return <ClineCreditsErrorView defaultFg={defaultFg} />;
}
return (
<box flexDirection="row">
<text fg="red" content="* " />