mirror of
https://github.com/getpaseo/paseo.git
synced 2026-10-23 17:52:49 +00:00
* 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.
90 lines
2.7 KiB
TypeScript
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;
|
|
}
|