Detect plan tier from .claude.json; seed sane caps per tier (Pro/Max 5x/Max 20x)

This commit is contained in:
megaproxy 2026-05-09 00:54:50 +01:00
parent 106ad28f9f
commit ef84257ddd
7 changed files with 236 additions and 7 deletions

View file

@ -5,14 +5,21 @@
disable as disableAutostart,
isEnabled as isAutostartEnabled,
} from "@tauri-apps/plugin-autostart";
import { getSettings, setSettings, listDistros, getRoots } from "../ipc";
import type { Settings, ResolvedRoots } from "../types";
import {
getSettings,
setSettings,
listDistros,
getRoots,
detectPlanTier,
} from "../ipc";
import type { Settings, ResolvedRoots, TierInfo } from "../types";
let { onClose }: { onClose: () => void } = $props();
let settings = $state<Settings | null>(null);
let distros = $state<string[]>([]);
let roots = $state<ResolvedRoots | null>(null);
let tier = $state<TierInfo | null>(null);
let busy = $state(false);
let err = $state<string | null>(null);
@ -21,6 +28,7 @@
settings = await getSettings();
distros = await listDistros();
roots = await getRoots();
tier = await detectPlanTier();
// Sync displayed autostart from the plugin's actual state.
const enabled = await isAutostartEnabled();
if (settings && settings.autostart !== enabled) {
@ -31,6 +39,17 @@
}
});
function applyTierCaps() {
if (!settings || !tier) return;
settings = {
...settings,
caps: {
block_tokens: tier.recommended_caps.block_tokens,
weekly_tokens: tier.recommended_caps.weekly_tokens,
},
};
}
async function save() {
if (!settings) return;
busy = true;
@ -59,12 +78,30 @@
{#if err}<div class="error">{err}</div>{/if}
{#if settings}
{#if tier}
<div class="tier-card">
<div class="row spread">
<div>
<div class="label" style="margin:0">Plan tier</div>
<div>{tier.label}</div>
</div>
<button onclick={applyTierCaps} title="Set caps to recommended values for this tier">
Use recommended
</button>
</div>
<div class="muted hint">
Approximate — Anthropic doesn't publish exact caps. Tune below
once you actually hit a limit.
</div>
</div>
{/if}
<div class="field">
<div class="label">5-hour block cap (tokens)</div>
<input
type="number"
min="0"
step="10000"
step="100000"
bind:value={settings.caps.block_tokens}
/>
</div>
@ -74,7 +111,7 @@
<input
type="number"
min="0"
step="100000"
step="1000000"
bind:value={settings.caps.weekly_tokens}
/>
</div>
@ -147,6 +184,16 @@
.actions { margin-top: auto; padding-top: 8px; border-top: 1px solid var(--border); }
.roots { margin: 0; padding-left: 14px; max-height: 60px; overflow: auto; }
.roots code { font-size: 10px; word-break: break-all; }
.tier-card {
background: var(--bg-card);
border: 1px solid var(--border);
border-radius: 6px;
padding: 8px;
display: flex;
flex-direction: column;
gap: 4px;
}
.hint { font-size: 10px; line-height: 1.3; }
.error {
background: rgba(255, 107, 107, 0.15);
border: 1px solid var(--danger);

View file

@ -1,6 +1,6 @@
import { invoke } from "@tauri-apps/api/core";
import { listen, type UnlistenFn } from "@tauri-apps/api/event";
import type { ResolvedRoots, Settings, UsageSnapshot } from "./types";
import type { ResolvedRoots, Settings, TierInfo, UsageSnapshot } from "./types";
export const getSnapshot = (): Promise<UsageSnapshot> => invoke("get_snapshot");
export const getSettings = (): Promise<Settings> => invoke("get_settings");
@ -10,6 +10,7 @@ export const listDistros = (): Promise<string[]> => invoke("list_distros");
export const getRoots = (): Promise<ResolvedRoots> => invoke("get_roots");
export const forceRescan = (): Promise<void> => invoke("force_rescan");
export const quitApp = (): Promise<void> => invoke("quit_app");
export const detectPlanTier = (): Promise<TierInfo> => invoke("detect_plan_tier");
export const onUsageUpdated = (
cb: (snap: UsageSnapshot) => void,

View file

@ -58,3 +58,13 @@ export interface ResolvedRoots {
wsl_distro: string | null;
native_present: boolean;
}
// Mirrors PlanTier in src-tauri/src/tier.rs. The Rust enum is serde-tagged
// externally, so the wire shape is `"Max5x"` for unit variants and
// `{ MaxOther: "raw-string" }` for tuple variants — we just keep it as
// `unknown` here and rely on `label` for display.
export interface TierInfo {
tier: unknown;
label: string;
recommended_caps: Caps;
}