/** * Kimi Quota Command (kb #62) — registers `/quota` on Adolf's Matrix channel. * * OpenClaw's native-command dispatch (`api.registerCommand`) runs a * `/`-prefixed command BEFORE the agent turn: no model is invoked, so this * never spends a Kimi turn (unlike asking Adolf in prose "what's my quota"). * It hits adolf-llm's own GET /usage route (server.js, kb #62 piece 1), which * itself talks straight to Kimi's managed-usage API — no LLM anywhere in the * path. * * Gating: `requireAuth: true` (the registerCommand default) restricts the * command to `ctx.isAuthorizedSender`, i.e. the same Matrix DM allowlist * (`channels.matrix.dm.allowFrom` in openclaw.json) that already gates every * other interaction with Adolf. No separate owner-only tier is needed here — * it's a read-only status line, not a privileged action. */ import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry"; // adolf-llm is a sibling service on the same `openai` compose network — // reached by service name, not localhost/host.docker.internal. const USAGE_URL = "http://adolf-llm:8010/usage"; const FETCH_TIMEOUT_MS = 5000; function pct(row) { return row && typeof row.pct === "number" ? `${row.pct}%` : "n/a"; } async function fetchUsage() { const controller = new AbortController(); const timer = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS); try { const res = await fetch(USAGE_URL, { signal: controller.signal }); const body = await res.json().catch(() => ({})); if (!res.ok) throw new Error(body?.error || `adolf-llm /usage HTTP ${res.status}`); return body; } finally { clearTimeout(timer); } } export default definePluginEntry({ id: "quota-command", name: "Kimi Quota Command", description: "LLM-free /quota command: reads Adolf's Kimi usage from adolf-llm:8010/usage and replies with a compact readout.", register(api) { api.registerCommand({ name: "quota", description: "Show Kimi quota usage (5h / weekly / 7d) — no model call.", acceptsArgs: false, requireAuth: true, handler: async () => { try { const usage = await fetchUsage(); const line = `Kimi: 5h ${pct(usage.window_5h)} · weekly ${pct(usage.weekly)} · 7d ${pct(usage.window_7d)}`; return { text: line, suppressReply: true }; } catch (e) { api.logger?.warn?.(`quota-command: fetch failed (${e?.message || e})`); return { text: `Kimi quota unavailable: ${e?.message || e}`, suppressReply: true }; } }, }); }, });