opencode prices and Go limits, settings out of .env

- Fetch prices for opencode (Zen and Go) incl. limits, image input, reasoning,
  long-context tiers and peak-hour/date factors; costs apply them per request
- Record the cost opencode reports per response; track Go allowance in 5h/7d/month
  windows against a provider plan limit (budget table, status overlay)
- Settings → Config file: move values of ${NAME} references into the config
- Examples, presets and forms keep keys in the config instead of .env

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Richard ZinkandClaude Opus 5.5 committed 2026-11-09 10:19:15 +01:00
1 parent 70367211c1
commit bfa1f641d7
23 files changed
+723 -69

No files matched your search

+48 -14
View File
@@ -1,3 +1,4 @@
import { Fragment } from "react";
import { Icon } from "./Icon";
import { useFetch } from "../hooks";
import { fmtCost, fmtPercent, useT } from "../i18n";
@@ -11,9 +12,32 @@ interface BudgetRow {
warning: boolean;
exceeded: boolean;
images?: boolean;
plan?: Plan;
remote?: { currency?: string; remaining?: number | null; limit?: number | null; used?: number | null; spent_month?: number; scope?: string; error?: string };
}
/** opencode Go: usage windows against the plan's limits (USD). */
export interface Plan {
limit: number;
windows: { window: "5h" | "week" | "month"; used: number; limit: number; ratio: number }[];
}
/** Three small meters (5 hours, week, month) for a subscription plan. */
export function PlanUsage({ plan }: { plan: Plan }) {
const t = useT();
return (
<div className="plan-usage">
{plan.windows.map((w) => (
<div key={w.window} className="plan-window" title={`${fmtCost(w.used, "USD")} / ${fmtCost(w.limit, "USD")}`}>
<span className="plan-label">{t(`budget.window.${w.window}`)}</span>
<Bar share={w.ratio} warning={w.ratio >= 0.8} exceeded={w.ratio >= 1} />
<span className="plan-pct">{fmtPercent(w.ratio)}</span>
</div>
))}
</div>
);
}
interface BudgetData {
currency: string;
total: BudgetRow;
@@ -62,20 +86,30 @@ export function BudgetPanel() {
</thead>
<tbody>
{rows.map((r) => (
<tr key={r.provider}>
<td>
{r.provider === "total" ? <b>{t("stats.total")}</b> : r.provider}
{r.images && <span className="chip" style={{ marginLeft: 6 }}>{t("nav.images")}</span>}
</td>
<td className="num">{fmtCost(r.spent, data.currency)}</td>
<td className="num">{r.budget ? fmtCost(r.budget, data.currency) : "–"}</td>
<td>{r.share !== null && <Bar share={r.share} warning={r.warning} exceeded={r.exceeded} />}</td>
<td className="num" style={{ color: r.exceeded ? "var(--red)" : r.warning ? "var(--accent2)" : undefined }}>
{r.remaining !== null ? fmtCost(r.remaining, data.currency) : "–"}
{r.share !== null && <span className="muted"> ({fmtPercent(r.share)})</span>}
</td>
<td>{r.provider === "total" ? null : <Remote r={r.remote} t={t} />}</td>
</tr>
<Fragment key={r.provider}>
<tr>
<td>
{r.provider === "total" ? <b>{t("stats.total")}</b> : r.provider}
{r.images && <span className="chip" style={{ marginLeft: 6 }}>{t("nav.images")}</span>}
</td>
<td className="num">{fmtCost(r.spent, data.currency)}</td>
<td className="num">{r.budget ? fmtCost(r.budget, data.currency) : "–"}</td>
<td>{r.share !== null && <Bar share={r.share} warning={r.warning} exceeded={r.exceeded} />}</td>
<td className="num" style={{ color: r.exceeded ? "var(--red)" : r.warning ? "var(--accent2)" : undefined }}>
{r.remaining !== null ? fmtCost(r.remaining, data.currency) : "–"}
{r.share !== null && <span className="muted"> ({fmtPercent(r.share)})</span>}
</td>
<td>{r.provider === "total" ? null : <Remote r={r.remote} t={t} />}</td>
</tr>
{r.plan && (
<tr className="plan-row">
<td className="muted">{t("budget.plan", { v: fmtCost(r.plan.limit, "USD") })}</td>
<td colSpan={5}>
<PlanUsage plan={r.plan} />
</td>
</tr>
)}
</Fragment>
))}
</tbody>
</table>