diff --git a/.env.example b/.env.example index 9a24010bbe..1e26254afd 100644 --- a/.env.example +++ b/.env.example @@ -2034,6 +2034,12 @@ APP_LOG_TO_FILE=true # open-sse/services/qwenTokenPlanQuotaFetcher.ts. Prefer the per-connection # Dashboard fields (qwenCloudCookie / qwenCloudSecToken) — these env vars are # global fallbacks. Cookie/sec_token are SENSITIVE session credentials. +# Getting the cookie: log in to home.qwencloud.com > Billing > Subscription, +# press F12 > Network, reload, filter by api.json, click any request to +# cs-data.qwencloud.com and copy the WHOLE Cookie value from Request Headers +# (it contains login_qwencloud_ticket). Paste it on ONE line — the value may +# contain '=' and ';'. It expires with the browser session; re-paste it when +# the dashboard reports an expired session. # QWEN_CLOUD_COOKIE= # QWEN_CLOUD_SEC_TOKEN= # QWEN_TOKEN_PLAN_HOST= diff --git a/docs/reference/ENVIRONMENT.md b/docs/reference/ENVIRONMENT.md index 19c8a7b135..52247513a3 100644 --- a/docs/reference/ENVIRONMENT.md +++ b/docs/reference/ENVIRONMENT.md @@ -1141,7 +1141,7 @@ Provider quota endpoints, network tunnels (Tailscale, Ngrok, MITM debug proxy), | `REDIS_URL` | `redis://localhost:6379` | `src/shared/utils/rateLimiter.ts` | Redis connection string for the rate limiter backend. | | `ALIBABA_CODING_PLAN_HOST` | _(production host)_ | `open-sse/services/bailianQuotaFetcher.ts` | Override the host used to fetch Alibaba Bailian coding-plan quotas. | | `ALIBABA_CODING_PLAN_QUOTA_URL` | derived from host | `open-sse/services/bailianQuotaFetcher.ts` | Full quota URL override for Alibaba Bailian. | -| `QWEN_CLOUD_COOKIE` | _(unset)_ | `open-sse/services/qwenTokenPlanQuotaFetcher.ts` | Console session cookie for the Qwen Cloud / Model Studio personal Token Plan quota gateway. Sensitive; prefer the per-connection `qwenCloudCookie` Dashboard field. | +| `QWEN_CLOUD_COOKIE` | _(unset)_ | `open-sse/services/qwenTokenPlanQuotaFetcher.ts` | Console session cookie for the Qwen Cloud / Model Studio personal Token Plan quota gateway (the inference API key cannot read it). Copy the whole `Cookie` request header — it contains `login_qwencloud_ticket` — from any `api.json` call to `cs-data.qwencloud.com` on home.qwencloud.com › Billing › Subscription (F12 › Network). Sensitive and session-scoped; prefer the per-connection `qwenCloudCookie` Dashboard field. | | `QWEN_CLOUD_SEC_TOKEN` | _(unset)_ | `open-sse/services/qwenTokenPlanQuotaFetcher.ts` | Manual `sec_token` override for the Token Plan console gateway. Sensitive; when unset the fetcher resolves it from the dashboard HTML using the cookie. | | `QWEN_TOKEN_PLAN_HOST` | `https://cs-data.qwencloud.com` | `open-sse/services/qwenTokenPlanQuotaFetcher.ts` | Gateway host override for the personal Token Plan quota fetcher (e.g. `bailian-singapore-cs.alibabacloud.com` for the Model Studio console). | | `QWEN_TOKEN_PLAN_DASHBOARD_URL` | `https://home.qwencloud.com/` | `open-sse/services/qwenTokenPlanQuotaFetcher.ts` | Dashboard URL used to resolve `sec_token` from the logged-in HTML. | diff --git a/open-sse/services/usage/qwen-token-plan.ts b/open-sse/services/usage/qwen-token-plan.ts index ed58fe26b7..72af5b4e9f 100644 --- a/open-sse/services/usage/qwen-token-plan.ts +++ b/open-sse/services/usage/qwen-token-plan.ts @@ -51,9 +51,13 @@ export async function getQwenTokenPlanUsage( if (!quota) { return { message: - "Qwen Token Plan connected. Quota requires a console session cookie " + - "(qwenCloudCookie in the connection settings or QWEN_CLOUD_COOKIE env) — " + - "the inference API key cannot read it. Refresh the cookie if it expired.", + "Qwen Token Plan connected. Quota needs a console session cookie — the inference " + + "API key cannot read it. Get it at home.qwencloud.com › Billing › Subscription " + + "(logged in): F12 › Network, reload, filter by api.json, click a request to " + + "cs-data.qwencloud.com and copy the whole Cookie value from Request Headers " + + "(it contains login_qwencloud_ticket). Paste it into the connection's " + + "'Qwen / Model Studio console cookie' field, or set QWEN_CLOUD_COOKIE. " + + "The cookie expires with the browser session — re-paste it when this message returns.", }; } diff --git a/src/app/(dashboard)/dashboard/providers/[id]/components/modals/QuotaScrapingFields.tsx b/src/app/(dashboard)/dashboard/providers/[id]/components/modals/QuotaScrapingFields.tsx index 52dd86013a..b43baba368 100644 --- a/src/app/(dashboard)/dashboard/providers/[id]/components/modals/QuotaScrapingFields.tsx +++ b/src/app/(dashboard)/dashboard/providers/[id]/components/modals/QuotaScrapingFields.tsx @@ -191,13 +191,17 @@ export default function QuotaScrapingFields({ type="password" value={values.qwenCloudCookie} onChange={(e) => onChange({ qwenCloudCookie: e.target.value })} - placeholder="token=..." + placeholder="cna=...; login_qwencloud_ticket=...; ..." hint={providerText( t, "qwenCloudCookieHint", - editMode - ? "Leave blank to keep the stored cookie. Required for Token Plan quota — the inference API key cannot read it. Paste the full Cookie header from home.qwencloud.com (or the Model Studio console)." - : "Required for Token Plan quota — the inference API key cannot read it. Paste the full Cookie header from home.qwencloud.com (or the Model Studio console)." + (editMode ? "Leave blank to keep the stored cookie. " : "") + + "Required for Token Plan quota — the inference API key cannot read it. " + + "How to get it: open home.qwencloud.com › Billing › Subscription while logged in, " + + "press F12 › Network, reload the page, filter by api.json, click any request to " + + "cs-data.qwencloud.com, then under Request Headers copy the WHOLE Cookie value " + + "(it contains login_qwencloud_ticket). It expires with the browser session — " + + "re-paste it when the quota reports an expired session." )} autoComplete="off" spellCheck={false}