Files
paseo/plugins/opencode-go-usage-source/server/usage.ts
T
Mohamed Boudra e939dd4c7a Streamline sidebar usage and stacked sheet interactions (#5805)
* feat(app): streamline sidebar usage and stacked sheets

* fix(app): restore compact sidebar navigation clicks

The close button container emitted box-none through Unistyles as invalid CSS and intercepted New workspace clicks. Let React Native apply click-through behavior at the view boundary. Keep the import journey on its existing New workspace screen and assert only the visible transcript.

* fix(app): cap menu sheets at eighty percent height

Keep long sidebar usage reports recognizably sheet-sized while allowing the content to scroll. Short menu sheets retain dynamic sizing.
2026-10-01 04:55:17 +01:00

90 lines
2.7 KiB
TypeScript

import { readFile } from "node:fs/promises";
import { homedir } from "node:os";
import { join } from "node:path";
import { z } from "zod";
import {
hashAccountKey,
toneFromUsedPct,
windowFromUsedPct,
type UsageReport,
} from "@getpaseo/plugin/server/usage";
import type { Input } from "../shared/input.js";
const authSchema = z
.object({
"opencode-go": z
.object({ type: z.literal("api"), key: z.string().min(1) })
.passthrough()
.optional(),
})
.passthrough();
const windowSchema = z.object({
status: z.enum(["ok", "rate-limited"]),
percent: z.number().finite(),
resetsAt: z.iso.datetime(),
});
const responseSchema = z.object({
usage: z.object({ rolling: windowSchema, weekly: windowSchema, monthly: windowSchema }),
});
function authPath(env: NodeJS.ProcessEnv = process.env): string {
return join(
env.XDG_DATA_HOME || join(env.HOME || homedir(), ".local", "share"),
"opencode",
"auth.json",
);
}
export async function readDefaultKey(path = authPath()): Promise<string | null> {
try {
const auth = authSchema.parse(JSON.parse(await readFile(path, "utf8")));
return auth["opencode-go"]?.key ?? null;
} catch {
return null;
}
}
export async function discover(path = authPath()): Promise<Array<{}>> {
return (await readDefaultKey(path)) ? [{}] : [];
}
export async function fetchUsage(
_input: Input,
fetchApi: typeof fetch = fetch,
path = authPath(),
): Promise<UsageReport> {
const apiKey = await readDefaultKey(path);
if (!apiKey) return { status: "unavailable", windows: [] };
const response = await fetchApi("https://opencode.ai/zen/go/v1/usage", {
headers: { Authorization: `Bearer ${apiKey}`, Accept: "application/json" },
signal: AbortSignal.timeout(15_000),
});
if (response.status === 401 || response.status === 403)
return { status: "unavailable", windows: [] };
if (!response.ok) throw new Error(`OpenCode Go usage API returned ${response.status}`);
const data = responseSchema.parse(await response.json());
const windows = (
[
// The rolling window's length is not reported, so its percent stands without a name.
["rolling", "Rolling", "", data.usage.rolling],
["weekly", "Weekly", "wk", data.usage.weekly],
["monthly", "Monthly", "mo", data.usage.monthly],
] as const
).map(([id, label, shortLabel, value]) =>
windowFromUsedPct({
id,
label,
shortLabel,
utilizationPct: value.percent,
resetsAt: value.resetsAt,
tone: toneFromUsedPct(value.percent),
}),
);
return { status: "available", planLabel: "Go", windows };
}
export async function identify(_input: Input, path = authPath()) {
const key = await readDefaultKey(path);
return key ? { key: hashAccountKey(key) } : null;
}