Skip to content

About

Loomy (iFlytek) provider plugin for OpenCode / magpie: phone-code sign-in, OpenAI-compatible gateway, dual point wallets, daily first-login refresh · 讯飞 Loomy 供应商插件

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Repository files navigation

@roadkid/opencode-loomy-auth

English | 中文

An OpenCode / magpie provider plugin for a Loomy (iFlytek) account. You sign in with Loomy's phone-code sign-in, your chats go through Loomy's OpenAI-compatible model gateway, and your points — the permanent balance and the daily grant, two wallets — and the daily first-login refresh that renews the grant land on the Usage card.

Provider id Sign-in Upstream hosts
loomy phone code (+86) loomyad.xunfei.cn (account plane account.xfinfr.com)

The package exports LoomyAuthPlugin (loomy).

Install

magpie plugin add github:road-kid/opencode-loomy-auth    # this repository
magpie plugin add @roadkid/opencode-loomy-auth  # the npm name
magpie plugin add .                                      # a local copy — no npm, no GitHub
magpie plugin login loomy      # the phone-code sign-in
magpie provider test loomy     # one small request per API, to check the wiring
magpie quota                   # the points card

The same package loads in OpenCode (the "plugin" array in opencode.json, then opencode auth login). magpie keeps the sign-in in plugin-auth.json (mode 600); OpenCode keeps it in auth.json.

Signing in

The sign-in is Loomy's phone-code one, in two steps:

  • magpie asks for the phone number first (mainland China, 11 digits; a leading +86 or 86, spaces and hyphens are cleaned off before the check);
  • the plugin sends the SMS code (it lives five minutes), magpie opens a six-digit code box, and the plugin trades the code for the session.

The session lasts fourteen days and Loomy offers no refresh. When it ends (HTTP 401/403, or the business codes 020002/100002), the plugin marks the sign-in expired and magpie asks for a new SMS sign-in — there is nothing to renew. The account's id is the masked phone (138****8000); the Loomy userid is kept with the credential.

The account host checks an HMAC-SHA1 signature on both sign-in calls. The signature belongs to the account plane alone — the gateway planes never sign.

Requests

Chats are OpenAI Chat (@ai-sdk/openai-compatible) sent to https://loomyad.xunfei.cn/api/v1/chat/completions, the request body passed through untouched. The session token rides in both headers the gateway reads — token: and Authorization: Bearer, the client's own dual write.

A refusal whose words say the points or quota ran out (a 402, 403 or 429 that says so) goes on as a 429 rate_limit_error, so magpie can move the conversation to another account. Any other 401/403 is the sign-in's end.

Models

Signed in, the model list is read live from GET /api/v1/models: capabilities become the tool / reasoning / image bits, the context and output limits pass through, and every model carries the low/medium/high reasoning-effort variants. The gateway prints each model's point rate as a suffix on its display name (MiniMax M3 (x4.0) — ASCII and full-width brackets both occur); the plugin splits that off — the name stays clean, and the rate rides the model record, the field the host's picker reads the multiplier from. A model the upstream itself declares free — the words 免费 in the name, or a rate of exactly zero — carries a free bit; no rate at all is not free. Rows that aren't chat models (the image generators say so) are left off the list: selecting one is selecting a failure.

The live list is the source of truth and wins the moment it answers. Without a session, on an empty answer or a failed ask, the plugin falls back to the built-in table in the code — a snapshot of this same catalog's text models as measured on 2026-10-28, aging only as the gateway's own catalog moves on; the snapshot's rates and free bits go back to the host with it, and a failed ask is marked as one, so the editor knows the list is the plugin's defaults and not the account's. The table below is what the gateway itself served on that day, kept here as a field guide:

Model Display name Rate Notes
spark-x2.6 Spark-X2.6 正式版(限时免费) x0.0 thinking
spark-x Spark-X2.5(限时免费) x0.0 thinking
DeepSeek-V4.1-Flash DeepSeek V4.1 Flash x2.0 thinking, image
MiniMax-M3 MiniMax M3 x4.0 thinking, image
Kimi-k2.6 Kimi k2.6 x6.5 thinking, image
qwen-3.8-max Qwen 3.8 Max x12.0 thinking
GLM-5.3-Flash GLM 5.3 Flash x0.8 thinking, image
qwen3.8-flash qwen 3.8 flash x0.8 thinking, image
mimo-v2.5 MiMo V2.5 x3.3 thinking, image, audio
auto 自动 · 均衡 — the gateway's own router
Hy-Image-3.5-preview Hy image 3.5 preview — image generation
doubao-seedream-5-lite Doubao seedream 5 lite — image generation
qwen-image-3.0-pro qwen image 3.0 pro — image generation

The three image-generation rows declare output_modalities: ["image"] — they draw pictures rather than answer in text, and how their answers ride the chat endpoint is unverified.

Usage

The usage card shows the two wallets Loomy keeps:

  • the balance line: permanent + daily grant, added together (the answer's own total when it names one);
  • a daily-grant window: the grant's consumed share as its percentage, with what is left over the limit beside it. dailyBalance is the grant's remaining (dailyQuota − dailyConsumed), so the percentage needs the grant's size: the pair the answer carries when it carries one, else the dailyQuota the check-in notes per account — the note lives on the credential itself (the host's own store, carried across plugin upgrades; a small file beside the plugin stands in where the host has no such store). A card that finds no note anywhere asks the first-login answer itself — once, the call being idempotent for the day (the client makes it on every login) — so a fresh install heals on its first render instead of waiting for the next check-in; no share is invented and no quota is assumed. The wording follows the interface language the host keeps in its settings (Chinese, or English beside it). The window carries the cycle's exact reset moment — the next Beijing midnight of the day the check-in's answer named — so the card counts down to the real refill, not a guessed one;
  • the permanent row set aside: the permanent balance, which has no window;
  • the newcomer row beside them: the one-time newcomer tasks (10000 points in all) while any stand unclaimed — an aside, never a percentage: a purse spent once has no window to consume. The check-in claims them; until it runs, the row says what is still claimable.

Loomy has no dedicated balance or check-in endpoint: the summary rides on the points-records call, and the "check-in" is the first-login call the Loomy client itself makes on every login. The server renews the daily grant once a day, and the answer's alreadyProcessed tells the day's renewal from a repeat: a repeat reports done ("今日已刷新", with what is left of the grant), and the day's first reports claimed — with the gain as its credit when the answer carries the whole arithmetic (dailyQuota − dailyConsumed), and the remaining in the message otherwise.

Check-in

The automatic daily run is magpie's, not the plugin's: the Daily check-in switch lives in Settings → Usage → Plugins, ships with magpie ≥ v0.1.1083 and is off by default — turn it on and magpie runs the refresh once a day per account (only a failed outcome retries that day). The Check in now button runs it then and there. OpenCode doesn't call this hook.

The same run also claims the one-time newcomer tasks (the eight worth 10000 points in all): serially, highest value first, idempotent — and a refusal among them never touches the daily claim's own result. The first check-in of a fresh account typically refreshes the grant and lands the newcomer purse in one go.

Known limits

  • The session is fourteen days, with no refresh. When it ends the only way on is a new SMS sign-in.
  • The point rate exists only as the display name's suffix. Loomy ships no structured rate field; the plugin splits the suffix off but cannot verify any amount against the catalog.
  • Image input differs per model family. Verified on the earlier catalog's deepseek-v4-flash-0731 (since retired): it read only the gateway's private <image_base64>…</image_base64> marker (a standard image_url is cut off as text), while the GLM models are the opposite — only image_url, and the marker breaks them. The rewrite is a planned second iteration; until it lands, image input is reliable on the GLM family alone, and the newer DeepSeek-V4.1-Flash declares image input whose shape on this gateway is unverified.
  • reasoning_effort is ignored on non-thinking models. Every model carries the low/medium/high variants the way the client hangs them; the upstream drops the field on the models that don't think — a known behavior. Which models think is whatever the live catalog declares — on 2026-10-28 most text models did, not only the spark pair.
  • Streaming tool calls are not yet verified on a real account. The desktop gateway's tool_calls streaming has no recorded real-machine run behind it; it is the first thing to check with your agent's tool loop. If it fails, the toolcall bit gets turned down in a future release.
  • The newcomer tasks are claimed by the check-in. The eight one-off tasks (10000 points in all) are queried and claimed on every check-in — serially, highest value first, and idempotent: a repeat answers alreadyCompleted and pays nothing twice. A refusal among them never touches the daily claim's own result, and their still-claimable sum shows on the card as an aside row while any stands. The upstream judges completion its own way; the keys and values are the client's own task registry's.

Disclaimer

This is an unofficial community package. It has nothing to do with iFlytek or Loomy. The implementation is reverse-engineered from the desktop client's own behavior and measured against it, for personal study and research. Your phone number, your code and your session go only to Loomy's own hosts. The points are for Loomy's own services; Loomy's terms decide where else they may be used, and iFlytek hasn't approved that. The risk to the account is yours. An upstream protocol change can break the package at any time.

Development

node --test                       # the unit tests (node:test, a local fake upstream, no network)
node scripts/check.node.mjs      # the shape check, the same logic the community CI runs

The plugin itself is one index.mjs with no runtime dependencies — only node:crypto and the standard fetch — and loads in Bun and Node ≥ 20.11 alike.

About

Loomy (iFlytek) provider plugin for OpenCode / magpie: phone-code sign-in, OpenAI-compatible gateway, dual point wallets, daily first-login refresh · 讯飞 Loomy 供应商插件

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages