Files
AgapHost/openai/quota-command-openclaw-plugin/index.js
alvis 4ac595a3a9 Adolf memory: migrate Cognee -> Hindsight + Kimi quota tooling
Memory migration (H1-H5, kb#73-77,84):
- hindsight service in openai/docker-compose.yml: LLM via Kimi (cognee-llm
  wrapper), multilingual GPU embeddings (bge-m3 via ollama), jina multilingual
  reranker; pg0 + model cache persisted
- openclaw.json/shared-mcp.json: mcp.servers cognee -> hindsight (bank "adolf")
- hindsight-openclaw-plugin: forced-hook memory (before_prompt_build recall +
  agent_end retain), replacing cognee's hook layer; cognify-sweep dropped
- verified live: Russian retain->recall, cross-session recall, bank isolation

Kimi quota (kb#62):
- adolf-llm/server.js: LLM-free GET /usage route (Kimi managed-usage API)
- quota-command-openclaw-plugin: /quota readout command

Cognee stack left running (decommission is H4/kb#76). Kimi-quota-footer
auto-append abandoned (streamed Matrix replies bypass outbound hooks).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014t8Qg9gi7H7HtT8MncoXAB
2026-07-15 19:53:21 +00:00

65 lines
2.5 KiB
JavaScript

/**
* 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 };
}
},
});
},
});