Skip to content

feat: add /quota slash command for GLM Coding Plan usage display#10

Merged
william0wang merged 3 commits into
mainfrom
feat/quota-command
Jul 7, 2026
Merged

feat: add /quota slash command for GLM Coding Plan usage display#10
william0wang merged 3 commits into
mainfrom
feat/quota-command

Conversation

@william0wang

Copy link
Copy Markdown
Owner

Summary

Adds a /quota slash command that queries the GLM Coding Plan usage API directly (no ZCode backend involvement) and renders a multi-line progress-bar card showing remaining usage across windows (5h / weekly / MCP).

  • New src/quota/ module (client, parse, format, cache, types, index) — pure, testable units with a single queryQuota() orchestration entry point.
  • Slash interception in src/handlers/slash.ts/quota is the exception: it never calls ZCode, always returns a status line (success card or an error fallback), and never throws.
  • Listed in SLASH_COMMANDS so it appears in the editor's / completion menu.

Behavior

  • Cached for 10s to debounce rapid repeats; cache is in-memory, process-wide.
  • Degrades gracefully: missing apiKey / network failure → unavailable; 401/auth text → auth_error; 429/rate-limit text → rate_limited.
  • Host routing mirrors the backend: api.z.ai baseURL → intl endpoint, else CN (open.bigmodel.cn). Credentials reuse the active provider's apiKey from ~/.zcode/v2/config.json.
  • Percentage computation has a tolerant fallback chain (remaining+currentValuecurrentValue/usage → legacy percentage), so inconsistent field sets across limit kinds still render.

Tests

  • tests/quota.test.ts — 26 tests covering parsing (percentage fallbacks, label derivation, error states), formatting (bar rendering, detail sub-lines), TTL cache, and queryQuota orchestration (cache hit / fetch failure → unavailable) with injected fakes (no real network).
  • Full suite: 126 tests passing, typecheck clean, 0 lint errors, build OK.

Checklist

  • pnpm typecheck
  • pnpm lint (0 errors)
  • pnpm test
  • pnpm build

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a new /quota slash command that queries the GLM Coding Plan usage API directly and displays the results as a formatted plain-text card. It includes an in-memory TTL cache to debounce requests, an HTTP client, robust response parsing with percentage fallbacks, and comprehensive unit tests. The review feedback suggests several improvements to robustness and type safety: defensively handling missing or malformed properties in item.detail to prevent runtime crashes, explicitly handling HTTP 401 and 403 status codes to correctly return authentication errors, and avoiding non-null assertions (!) when checking payload data.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread src/quota/format.ts
Comment on lines +92 to +99
if (item.detail && item.detail.length > 0) {
const last = item.detail.length - 1;
item.detail.forEach((d, i) => {
const branch = i === last ? "└" : "├";
const name = d.modelCode.padEnd(DETAIL_LABEL_WIDTH);
lines.push(` ${branch} ${name}${d.usage}`);
});
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Defensively handle potential missing or malformed properties in item.detail elements (such as d.modelCode or d.usage) to prevent runtime TypeError crashes if the API response structure changes or contains unexpected values.

  if (item.detail && item.detail.length > 0) {
    const last = item.detail.length - 1;
    item.detail.forEach((d, i) => {
      if (!d) return;
      const branch = i === last ? "└" : "├";
      const modelCode = typeof d.modelCode === "string" ? d.modelCode : "unknown";
      const usage = typeof d.usage === "number" ? d.usage : 0;
      const name = modelCode.padEnd(DETAIL_LABEL_WIDTH);
      lines.push(`  ${branch} ${name}${usage}`);
    });
  }

Comment thread src/quota/parse.ts
Comment on lines +184 to +187
// HTTP-level rate limit.
if (envelope.status === 429 || isRateLimitedMessage(envelope.text)) {
return { kind: "rate_limited" };
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Explicitly handle HTTP 401 (Unauthorized) and 403 (Forbidden) status codes at the top of parseQuotaEnvelope. If the API gateway returns these status codes with an empty or non-JSON body, the current implementation would fall through and return unavailable instead of auth_error.

  // HTTP-level rate limit or auth errors.
  if (envelope.status === 429 || isRateLimitedMessage(envelope.text)) {
    return { kind: "rate_limited" };
  }
  if (envelope.status === 401 || envelope.status === 403) {
    return { kind: "auth_error" };
  }

Comment thread src/quota/parse.ts
return { kind: "unavailable" };
}

const limits = Array.isArray(payload.data?.limits) ? payload.data!.limits! : [];

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Avoid using non-null assertion operators (!) by safely checking if payload.data and payload.data.limits exist. This improves type safety and readability.

Suggested change
const limits = Array.isArray(payload.data?.limits) ? payload.data!.limits! : [];
const limits = payload.data && Array.isArray(payload.data.limits) ? payload.data.limits : [];

Only freshly created sessions (session/new) are eligible for auto-title
on first end_turn. Resumed/loaded sessions already carry a title from
their history; the previous set-once gate only guarded within-session
re-entry, not the first message after a resume/load, so it would clobber
the existing title. Add a titleEligibleSessions set populated only by
newSession to gate the title write.
The queryQuota orchestration tests called loadZcodeCredentials() at runtime,
which reads ~/.zcode/v2/config.json. Locally that file exists, but CI has no
such file, so ANTHROPIC_API_KEY came back empty and fetchQuotaResponse threw
'no apiKey' before fetch was ever called — landing as 'unavailable' and the
fetch spy recording zero calls. Mock the credentials module so the tests are
environment-independent.
@william0wang william0wang merged commit afd8f80 into main Jul 7, 2026
1 check passed
@william0wang william0wang deleted the feat/quota-command branch July 7, 2026 12:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant