Skip to content

feat(providers): Z.ai Start Plan provider (OAuth login, in-process traceless captcha, gateway wire) - #4647

Draft
alexx-ftw wants to merge 7 commits into
lidge-jun:devfrom
alexx-ftw:feat/zai-start-plan
Draft

alexx-ftw wants to merge 7 commits into
lidge-jun:devfrom
alexx-ftw:feat/zai-start-plan

Conversation

@alexx-ftw

@alexx-ftw alexx-ftw commented Sep 14, 2026 •

Copy link
Copy Markdown

Z.ai Start Plan provider

Adds a native zcode-start-plan provider that serves the Z.ai Start Plan quota from the ZCode plan gateway (zcode.z.ai/api/v1/zcode-plan/anthropic) — without requiring the ZCode desktop app. Login is OpenCodex's own OAuth flow against the gateway's CLI OAuth endpoints.

OAuth login

  • ocx login zcode-start-plan → browser authorize → poll → plan JWT stored in the ocx auth store (multi-account ready).
  • The JWT carries no exp claim and has no silent refresh; gateway rejections surface as terminal needsReauth → re-login.

Wire shape (mirrors the official client)

  • POST …/zcode-plan/anthropic/v1/messages, Anthropic format, Authorization: Bearer <jwt> + anthropic-version only — this route is exempt from the client's V4 request signing.
  • Identity + attribution headers mirror the client: User-Agent: ZCode/<ver> ai-sdk/anthropic/3.0.81, X-Title: Z Code@cli, X-ZCode-Agent: glm last, per-request x-request-id/x-zcode-trace-id, x-zcode-session-type: main.
  • Body inspection: the gateway rejects requests without the official ZCode system blocks (biz 3012). The adapter prepends them (powered-by line merged into the Environment block), applies the client's two-phase cache_control marking, and injects metadata.user_id from the JWT.

Aliyun WAF captcha

  • Challenges arrive as biz 3007 in the body or a non-empty x-aliyun-captcha-verify-param response header. The adapter mints a verify param with an in-process happy-dom traceless solver (deterministic fingerprint — randomization triggers F001 — gateway cookie priming, CDN cache, guest-realm timer scoping, stall detection) and replays the request once with X-Aliyun-Captcha-Verify-Param/-Region.
  • The solver runs inside a dedicated worker thread: the guest SDK's window aliasing, process-wide exception handling, synchronous Atomics.wait, and any global mutation are confined to that thread — a crash or hang fails only the pending solve.
  • Biz errors inside HTTP 200 (e.g. 1005 exceed quota limit — a per-window rate limit, not plan exhaustion) are mapped to real statuses (429/502) instead of surfacing as truncated streams. A 3012 WAF block surfaces as upstream_error.

Quota

Per-account probe of billing/balance (requires the X-Device-Mid header — its absence answers biz 3001 — persisted per install under the OpenCodex home, ZCODE_DEVICE_MID overrides) surfacing balance rows as custom quota windows.

GUI — request log attribution

Request Logs with provider and account columns

Request Logs gain an Account column resolving the opaque per-account log labels (o<hash>, p<random>) to emails/plan via the new read-only GET /api/account-labels (emails masked per privacy.maskEmails; Cache-Control: no-store).

Registry

  • Featured OAuth preset on the anthropic-compatible gateway route.
  • Models GLM-5.3, GLM-5.3-Flash (text+image), GLM-5.2, GLM-5-Turbo; 1M/200K context windows.
  • liveModels: false — the route has no /models listing; the list is the client-config allowlist.

Dependency

  • happy-dom — in-process captcha solver runtime (no browser, no headless Chrome).

Tests

tests/providers/zcode-start-plan.test.ts (14 cases: identity headers, trace headers, challenge detection, body transform incl. Claude-block stripping and caller-content coercion, label mapping) + transport/layout suites.

Validation

Validated live against the gateway: OAuth login, model turns (200 + streaming with message_stop), quota probe, and recovery after per-window rate limits.

Maintainer labels needed (per the PR quality gates)

The two failing checks are label-gated by design and need maintainer action:

  • new_suppression → suppression-approved: the vendored in-process captcha solver (src/adapters/zcode-start-plan/captcha-solver.ts, ~2.3k lines ported from a proven implementation) carries a top-level @ts-nocheck like its source; typing the port fully is follow-up work rather than review noise here.
  • unsponsored_surface → maintainer-sponsored: touches a management route (GET /api/account-labels, read-only) and the dependency files (happy-dom for the solver runtime).

Everything else the gates check is addressed in-branch: targets dev, no empty catch blocks, bounded fetches with abort/timeout propagation, screenshot above.

Review readiness checklist

This PR stays in draft until every box below is ticked. Tick all four boxes once the requirements are met:

  • Required local validation passed; commands, results, and any full-suite exception are documented.

  • I pushed my PR to the latest dev commit.

  • I resolved all correct Codex and CodeRabbit findings.

  • My PR is ready for review.

Summary by CodeRabbit

  • New Features

    • Added support for the ZCode Start Plan provider, including OAuth sign-in, model selection, image input for supported models, and quota visibility.
    • Added account attribution to request logs, showing associated account details and plan when available.
    • Added localized “Account” column labels across supported languages.
  • Bug Fixes

    • Improved handling of provider authentication challenges and upstream errors for more reliable requests.

Loading
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request intake: hygiene-blocked Deterministic PR hygiene checks failed priority: P3 Low: new provider/client integration, large or experimental feature (>2000 LOC or >50 files), RFC/ro

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants