@inferhub/opencode-usage 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,31 @@
1
+ import { type AccountUsage } from "@inferhub/usage-client";
2
+ type Opts = {
3
+ apiKey?: string;
4
+ baseUrl?: string;
5
+ window?: "day" | "24h" | "7d" | "30d";
6
+ lowBalanceUsd?: number;
7
+ };
8
+ type PluginCtx = {
9
+ client?: {
10
+ app?: {
11
+ log?: (input: {
12
+ body: Record<string, unknown>;
13
+ }) => Promise<void> | void;
14
+ };
15
+ };
16
+ };
17
+ type PluginHooks = {
18
+ tool?: Record<string, {
19
+ description: string;
20
+ args?: Record<string, unknown>;
21
+ execute: (...args: unknown[]) => Promise<string> | string;
22
+ }>;
23
+ event?: (input: {
24
+ event: {
25
+ type: string;
26
+ };
27
+ }) => Promise<void> | void;
28
+ };
29
+ export declare function getLastUsage(): AccountUsage | null;
30
+ export declare function InferHubUsagePlugin(ctx: PluginCtx, options?: Opts): Promise<PluginHooks>;
31
+ export default InferHubUsagePlugin;
package/dist/index.js ADDED
@@ -0,0 +1,57 @@
1
+ import { fetchAccountUsage, formatReport, money, normalizeBaseUrl, resolveAuth, } from "@inferhub/usage-client";
2
+ let lastUsage = null;
3
+ async function loadUsage(opts = {}) {
4
+ const auth = opts.apiKey
5
+ ? { apiKey: opts.apiKey, baseUrl: normalizeBaseUrl(opts.baseUrl), source: "options" }
6
+ : resolveAuth({ apiKey: opts.apiKey, baseUrl: opts.baseUrl });
7
+ if (!auth) {
8
+ throw new Error("No InferHub API key. Configure provider.inferhub in opencode.json or set INFERHUB_API_KEY.");
9
+ }
10
+ const usage = await fetchAccountUsage({
11
+ apiKey: auth.apiKey,
12
+ baseUrl: auth.baseUrl,
13
+ window: opts.window ?? "day",
14
+ });
15
+ lastUsage = usage;
16
+ return usage;
17
+ }
18
+ export function getLastUsage() {
19
+ return lastUsage;
20
+ }
21
+ export async function InferHubUsagePlugin(ctx, options) {
22
+ const opts = options ?? {};
23
+ const low = opts.lowBalanceUsd ?? 0.5;
24
+ void loadUsage(opts).catch(() => { });
25
+ return {
26
+ tool: {
27
+ inferhub_usage: {
28
+ description: "Fetch InferHub account balance, day/all-time spend, and top used model for the current user",
29
+ args: {},
30
+ async execute() {
31
+ const usage = await loadUsage(opts);
32
+ return formatReport(usage) + "\n\n" + JSON.stringify(usage, null, 2);
33
+ },
34
+ },
35
+ },
36
+ event: async ({ event }) => {
37
+ if (event.type === "session.idle" || event.type === "session.created") {
38
+ try {
39
+ const usage = await loadUsage(opts);
40
+ if (Number(usage.balance.amount_usdc) < low) {
41
+ await ctx.client?.app?.log?.({
42
+ body: {
43
+ service: "inferhub-usage",
44
+ level: "warn",
45
+ message: `Low InferHub balance: ${money(usage.balance.amount_usdc)}`,
46
+ },
47
+ });
48
+ }
49
+ }
50
+ catch {
51
+ /* ignore */
52
+ }
53
+ }
54
+ },
55
+ };
56
+ }
57
+ export default InferHubUsagePlugin;
package/dist/tui.d.ts ADDED
@@ -0,0 +1,29 @@
1
+ type TuiApi = {
2
+ ui: {
3
+ toast: (input: {
4
+ variant?: string;
5
+ title?: string;
6
+ message: string;
7
+ duration?: number;
8
+ }) => void;
9
+ };
10
+ slots: {
11
+ register: (plugin: {
12
+ name?: string;
13
+ slots?: Record<string, (props: any) => any>;
14
+ }) => string;
15
+ };
16
+ event: {
17
+ on: (type: string, handler: (event: any) => void) => () => void;
18
+ };
19
+ lifecycle: {
20
+ onDispose: (fn: () => void) => () => void;
21
+ };
22
+ state: {
23
+ session: {
24
+ get: (id: string) => any;
25
+ };
26
+ };
27
+ };
28
+ export declare function tui(api: TuiApi): Promise<void>;
29
+ export default tui;
package/dist/tui.js ADDED
@@ -0,0 +1,96 @@
1
+ /**
2
+ * OpenCode TUI plugin — registers sidebar/home footer slots with InferHub usage.
3
+ *
4
+ * Install:
5
+ * opencode plugin opencode-inferhub-usage --global
6
+ * # or in opencode.json:
7
+ * { "plugin": ["opencode-inferhub-usage", "opencode-inferhub-usage/tui"] }
8
+ *
9
+ * Note: TUI plugins require OpenCode's TUI module export (`./tui`). If the
10
+ * runtime version only supports server plugins, the server tool still works.
11
+ */
12
+ import { fetchAccountUsageAuto, formatStatusLine, money, shortModel, } from "@inferhub/usage-client";
13
+ let cached = null;
14
+ let sessionCost = null;
15
+ let sessionModel = null;
16
+ async function refresh() {
17
+ try {
18
+ const { usage } = await fetchAccountUsageAuto({ window: "day" });
19
+ cached = usage;
20
+ if (Number(usage.balance.amount_usdc) < 0.5) {
21
+ // toast if available
22
+ }
23
+ return usage;
24
+ }
25
+ catch {
26
+ return null;
27
+ }
28
+ }
29
+ function line() {
30
+ if (!cached)
31
+ return "IH · loading…";
32
+ return formatStatusLine(cached, {
33
+ sessionCostUsd: sessionCost,
34
+ model: sessionModel,
35
+ });
36
+ }
37
+ export async function tui(api) {
38
+ void refresh();
39
+ const timer = setInterval(() => void refresh(), 60_000);
40
+ const offIdle = api.event.on("session.idle", () => void refresh());
41
+ const offUpd = api.event.on("session.updated", (ev) => {
42
+ const info = ev?.properties?.info ?? ev?.info ?? {};
43
+ if (typeof info.cost === "number")
44
+ sessionCost = info.cost;
45
+ if (typeof info.model === "string")
46
+ sessionModel = info.model;
47
+ if (info.model?.id)
48
+ sessionModel = info.model.id;
49
+ });
50
+ api.lifecycle.onDispose(() => {
51
+ clearInterval(timer);
52
+ offIdle();
53
+ offUpd();
54
+ });
55
+ // Prefer slots when the host supports Solid slot plugins.
56
+ try {
57
+ api.slots.register({
58
+ name: "inferhub-usage",
59
+ // Hosts that accept render functions will display these; others ignore.
60
+ slots: {
61
+ home_footer: () => line(),
62
+ sidebar_footer: () => {
63
+ if (!cached)
64
+ return "IH …";
65
+ return `bal ${money(cached.balance.amount_usdc)} · all ${money(cached.all_time.spend_usdc)} · top ${shortModel(cached.top_model?.model)}`;
66
+ },
67
+ sidebar_content: () => {
68
+ if (!cached)
69
+ return "InferHub usage loading…";
70
+ const sess = sessionCost != null ? money(sessionCost) : "—";
71
+ return `IH session ${sess} · day ${money(cached.window.spend_usdc)} · ${cached.window.requests} req`;
72
+ },
73
+ },
74
+ });
75
+ }
76
+ catch {
77
+ // Slot API unavailable — server tool still works.
78
+ }
79
+ // Toast on first successful load when balance is low.
80
+ void refresh().then((u) => {
81
+ if (u && Number(u.balance.amount_usdc) < 0.5) {
82
+ try {
83
+ api.ui.toast({
84
+ variant: "warning",
85
+ title: "InferHub",
86
+ message: `Low balance: ${money(u.balance.amount_usdc)}`,
87
+ duration: 5000,
88
+ });
89
+ }
90
+ catch {
91
+ /* ignore */
92
+ }
93
+ }
94
+ });
95
+ }
96
+ export default tui;
package/package.json ADDED
@@ -0,0 +1,60 @@
1
+ {
2
+ "name": "@inferhub/opencode-usage",
3
+ "version": "0.1.0",
4
+ "description": "OpenCode plugin: InferHub balance, session/day/all-time usage, top model",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.js"
12
+ },
13
+ "./tui": {
14
+ "types": "./dist/tui.d.ts",
15
+ "import": "./dist/tui.js"
16
+ }
17
+ },
18
+ "files": [
19
+ "dist"
20
+ ],
21
+ "scripts": {
22
+ "build": "tsc -p tsconfig.json",
23
+ "typecheck": "tsc -p tsconfig.json --noEmit",
24
+ "prepublishOnly": "npm run build"
25
+ },
26
+ "dependencies": {
27
+ "@inferhub/usage-client": "^0.1.0"
28
+ },
29
+ "peerDependencies": {
30
+ "@opencode-ai/plugin": ">=1.0.0"
31
+ },
32
+ "peerDependenciesMeta": {
33
+ "@opencode-ai/plugin": {
34
+ "optional": true
35
+ }
36
+ },
37
+ "devDependencies": {
38
+ "@types/node": "^22.15.0",
39
+ "typescript": "^5.8.2"
40
+ },
41
+ "engines": {
42
+ "node": ">=18"
43
+ },
44
+ "license": "MIT",
45
+ "keywords": [
46
+ "opencode",
47
+ "opencode-plugin",
48
+ "inferhub",
49
+ "usage"
50
+ ],
51
+ "publishConfig": {
52
+ "access": "public"
53
+ },
54
+ "repository": {
55
+ "type": "git",
56
+ "url": "git+https://github.com/inferhub/plugins.git",
57
+ "directory": "packages/opencode"
58
+ },
59
+ "homepage": "https://inferhub.dev"
60
+ }