-- Command Code quota — multi-account plan balance for the gateway. -- -- The billing plugin only ever reports what the gateway already measured, so it -- never has to reach out. A plan balance is different: the number only exists -- on the vendor's side, and the only way to learn it is to ask. Command Code -- exposes /alpha/billing/credits (plus whoami / subscriptions / usage) on the -- provider host, authenticated with the same API key the source already uses. -- -- Accounts: one per configured source whose base_url is the Command Code API -- host. Two keys on the same host are two separate accounts, and both are -- shown — an operator with a spare seat needs to see both draining. -- -- Every endpoint degrades independently. A vendor that retires one route must -- not blank the others, and a key that has run out still returns HTTP 401 on -- the inference path while credits reads fine, so the report has to survive -- partial failure and say WHICH part failed rather than rendering a zero that -- looks like "no quota left". local PLUGIN = {} PLUGIN.name = "ccquota" PLUGIN.version = "1.0.0" PLUGIN.description = "Command Code 多账户额度余量(5 小时 / 周 / 月窗口 + 余额)" -- Command Code's API host. __llmsproxy_http only permits hosts that a -- configured source already talks to, so this works exactly when such a source -- exists — no separate allowlist to keep in sync, and no way to widen reach. local API_HOST = "api.commandcode.ai" -- These headers are required alongside the bearer token. Without them the -- endpoints answer 401 even with a perfectly valid key, which looks identical -- to a revoked key and costs a lot of time to tell apart. -- -- The bearer token is NOT here on purpose: it is per-account, so fetchJSON -- copies this table and injects it per request. An earlier version passed this -- table straight through and every endpoint answered 401 — which reads exactly -- like a revoked key, and sent me looking at the account instead of the plugin. local CLI_VERSION = "1.54.2" local REQUIRED_HEADERS = { ["x-command-code-version"] = CLI_VERSION, ["x-cli-environment"] = "production", ["User-Agent"] = "commandcode-quota/" .. CLI_VERSION, } -- Accounts are handed in from the page (which reads /api/sources and its own -- key store). The kernel deliberately does NOT let a plugin read the config, so -- this is the only channel — which also means the key never sits in Lua state -- or in the persisted plugin file. -- Monthly credit caps per planId. -- -- The credits endpoint reports what is LEFT but never the cap, so a percentage -- is not derivable from it alone. The caps below come from the vendor's own -- pricing page: every plan costs $10/month and grants a different credit pool -- ($10 Go, $70 GOAT, $70 Pro, $70 Max). totalMonthlyCredits + monthlyCredits -- also sums to the cap, so this table is a CROSS-CHECK on the plan, not the only -- source — if the two disagree, PLAN_CAPS_WAIVED shows it instead of silently -- displaying a wrong percentage. -- -- A planId absent from this table renders the balance with no percentage and no -- bar. Guessing a cap for an unknown plan would be worse than showing none. local PLAN_CAPS = { ["individual-go"] = 10, ["individual-goat"] = 70, ["individual-pro"] = 70, ["individual-max"] = 70, ["team-goat"] = 70, } local ACCOUNTS = {} -- ---------------------------------------------------------------- helpers local function tonum(v) return tonumber(v) or 0 end -- usd formats a dollar amount the way the vendor's panel does: two decimals, -- but never a bare "0" when there is a real fractional balance left. A $0.0811 -- balance rounds to $0.08, and showing "$0" there would read as "nothing left". -- fetchJSON performs one request and returns (decodedTable, nil) or -- (nil, errorMessage). It NEVER raises: an escaping error inside a plugin hook -- can take the whole gateway down (see the hook-guard note in the kernel), so -- every failure has to arrive as an ordinary value. local function fetchJSON(path, key) -- Copy, then add the token: mutating the shared table would leak one -- account's key into every later request. local headers = {} for k, v in pairs(REQUIRED_HEADERS) do headers[k] = v end headers["Authorization"] = "Bearer " .. tostring(key or "") local res, err = __llmsproxy_http({ url = "https://" .. API_HOST .. path, method = "GET", headers = headers, timeout_ms = 12000, }) if res == nil then return nil, tostring(err or "request failed") end -- Body arrives as a JSON string; the kernel's json.decode is the only parser -- available and it raises on malformed input, so it is pcall'd here too. local ok, decoded = pcall(json.decode, res.body) if not ok then return nil, "bad JSON from " .. path end return decoded, nil end -- authFailure recognises the one error that means "this key is not usable" -- rather than "this endpoint is unhappy". Quota exhaustion and a revoked key -- both surface as 401 here, and they need opposite operator responses. -- ---------------------------------------------------------------- account report -- collectAccount gathers one account's numbers. Each endpoint is independent: -- credits is the one that matters, and the rest enrich it. A failure is -- recorded per-endpoint instead of aborting, because "I could not read the -- subscription" and "the account has no quota" must not look the same. local function collectAccount(acct) local report = { name = acct.name, label = acct.label, failures = {}, checked_at = os.time(), } local who, werr = fetchJSON("/alpha/whoami", acct.key) if who == nil then report.failures[#report.failures + 1] = "whoami: " .. tostring(werr) elseif type(who) == "table" and type(who.user) == "table" then report.email = who.user.email report.user = who.user.name or who.user.userName end local creds, cerr = fetchJSON("/alpha/billing/credits", acct.key) local rawStatus = 0 if creds == nil then report.failures[#report.failures + 1] = "credits: " .. tostring(cerr) else local c = creds.credits or {} local wl = creds.windowLimits or {} report.monthly = tonum(c.monthlyCredits) report.purchased = tonum(c.purchasedCredits) report.free = tonum(c.freeCredits) report.total = report.monthly + report.purchased + report.free -- belowThreshold is the vendor's own "running low" flag, distinct from -- whether any balance remains: an account can hold $3 and still be below -- their configured threshold. report.below_threshold = c.belowThreshold == true report.threshold = tonum(c.creditThreshold) report.limited = wl.limited == true report.exceeded = wl.exceeded report.fiveHour = wl.fiveHour report.weekly = wl.weekly rawStatus = 200 end local subs, serr = fetchJSON("/alpha/billing/subscriptions", acct.key) if subs == nil then report.failures[#report.failures + 1] = "subscriptions: " .. tostring(serr) elseif type(subs) == "table" and type(subs.data) == "table" then report.plan_status = subs.data.status report.plan_id = subs.data.planId report.plan_cap = PLAN_CAPS[tostring(subs.data.planId or "")] report.period_start = subs.data.currentPeriodStart report.period_end = subs.data.currentPeriodEnd report.cancel_at_period_end = subs.data.cancelAtPeriodEnd == true local md = subs.data.metadata if type(md) == "table" then report.plan_name = md.plan or md.name or md.tier end end local usage, uerr = fetchJSON("/alpha/usage/summary", acct.key) if usage == nil then report.failures[#report.failures + 1] = "usage: " .. tostring(uerr) elseif type(usage) == "table" then report.requests = tonum(usage.totalCount) report.spent = tonum(usage.totalCost) report.tokens = tonum(usage.totalTokens) report.success_rate = tonum(usage.successRate) -- usage/summary counts over the BILLING PERIOD, so this pairs with the -- monthly allowance rather than with the rolling 5h/weekly windows. report.monthly_used = tonum(usage.totalMonthlyCredits) report.period_basis = usage.periodBasis end -- Cross-check the configured cap against what the vendor spent plus what is -- left. A mismatch means PLAN_CAPS drifted from the real plan (a price change, -- or a planId this table does not know) — surfaced rather than papered over, -- because a confidently wrong "99% used" is the worst possible reading here. if report.plan_cap and report.monthly_used and report.monthly_used > 0 then local implied = report.monthly_used + report.total local drift = math.abs(implied - report.plan_cap) if drift > (report.plan_cap * 0.02 + 0.5) then report.cap_mismatch = true report.implied_cap = implied end end -- An account whose every endpoint failed is an auth problem, not a data -- problem: state it plainly so nobody reads "—" as "zero quota". if #report.failures >= 3 and report.total == nil then report.auth_failed = true end return report end -- ---------------------------------------------------------------- page PLUGIN.ui = { page = { page_id = "ccquota", title = "CC Quota", order = 45, mount = [==[
Command Code 额度
点击刷新以拉取各账户余量
尚未拉取
]==], }, } -- exports is what makes collect_account reachable from the plugin's own page. -- Without this list the call endpoint refuses it: the endpoint is an HTTP -- surface into the Lua state, so "which functions may it reach" has to be the -- plugin's explicit answer rather than a default. PLUGIN.exports = { "collect_account" } -- collect_account gathers one account's report. It returns the report TABLE (not -- a string) so the page receives structured numbers and booleans instead of -- having to parse anything back out of a formatted blob. PLUGIN.collect_account = function(acct) if type(acct) ~= "table" then return nil, "expected account table" end if acct.key == nil or acct.key == "" then return { name = tostring(acct.name or ""), failures = { "missing api key" }, auth_failed = true } end local rep = collectAccount({ name = acct.name, label = acct.label, key = acct.key, }) return rep, nil end return PLUGIN