ai: migrate LLM backbone from Kimi CLI to Codex CLI
Retires the Moonshot/Kimi subscription in favour of the already-paid ChatGPT plan. Both CLI wrappers now run `codex exec`; the kimi-agent container is gone. adolf-llm + hindsight-llm: - runKimi -> runCodex (`codex exec --json --skip-git-repo-check`), resume via `codex exec resume <thread_id>`. - MCP moves from a per-session .mcp.json (a workaround for Kimi having no --mcp-config-file flag) to a $CODEX_HOME/config.toml generated once at startup from shared-mcp.json. Field translation is load-bearing: bearerTokenEnvVar -> bearer_token_env_var, enabledTools -> enabled_tools. - approval_policy="never" + sandbox_mode required, or unattended turns block on an approval prompt nobody can answer. kimi-agent removed. It was the ONLY large-tier deployment behind LiteLLM, so deleting it outright would have silently degraded every large-tier request to the local 4B model via the existing fallbacks. tier-large, the auto_router complex-reasoning route and their fallbacks now point at the codex-backed adolf-llm wrapper (model_name: codex-agent). Three environment blockers fixed along the way: - OpenAI geo-blocks this host (403 unsupported_country_region_territory). Both containers now egress via the host xray proxy, with NO_PROXY keeping MCP and *.alogins.net traffic off the tunnel. - node:22-slim ships no system CA store; the Rust codex binary validates TLS against it, so every HTTPS call failed with a generic transport error while Node's own fetch worked. ca-certificates added to both images. - `codex exec resume` rejects -C/--cd (plain `codex exec` accepts it), which broke follow-up turns while first turns succeeded. Known regression: Kimi's managed-usage API has no Codex equivalent, so the /usage route returns 501 and there is no quota probe for the codex model. The two quota plugins degrade quietly to no output. Also: stop tracking cognee.env (live LLM + JWT secrets) and gitignore it. The secrets remain in earlier history and should be rotated. Verified live: plain turn, SSE streaming, session resume, MCP tool call, bearer-token MCP call, and completions through both LiteLLM routes. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014Y5QPagv4iun1ghpwM96Ff
This commit is contained in:
64
ai/quota-command-openclaw-plugin/index.js
Normal file
64
ai/quota-command-openclaw-plugin/index.js
Normal file
@@ -0,0 +1,64 @@
|
||||
/**
|
||||
* 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 };
|
||||
}
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
13
ai/quota-command-openclaw-plugin/openclaw.plugin.json
Normal file
13
ai/quota-command-openclaw-plugin/openclaw.plugin.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"id": "quota-command",
|
||||
"name": "Kimi Quota Command",
|
||||
"description": "Registers /quota: a native-command handler (runs before the agent, zero model calls) that reads Adolf's Kimi usage from adolf-llm:8010/usage and replies with a compact 5h/weekly/7d readout.",
|
||||
"activation": {
|
||||
"onStartup": true
|
||||
},
|
||||
"configSchema": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {}
|
||||
}
|
||||
}
|
||||
18
ai/quota-command-openclaw-plugin/package.json
Normal file
18
ai/quota-command-openclaw-plugin/package.json
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"name": "openclaw-quota-command",
|
||||
"version": "1.0.0",
|
||||
"description": "LLM-free /quota command for Adolf: reads Kimi usage from adolf-llm:8010/usage and replies with a compact readout.",
|
||||
"type": "module",
|
||||
"private": true,
|
||||
"main": "./index.js",
|
||||
"peerDependencies": {
|
||||
"openclaw": ">=2026.3.0"
|
||||
},
|
||||
"openclaw": {
|
||||
"extensions": ["./index.js"],
|
||||
"compat": {
|
||||
"pluginApi": ">=2026.0.0",
|
||||
"minGatewayVersion": "2026.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user