qwenproxy-cli 1.2.10 → 1.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +3 -3
- package/src/api/server.ts +14 -2
- package/src/core/accounts.ts +118 -17
- package/src/core/local-auth.ts +52 -0
- package/src/core/paths.ts +3 -0
- package/src/login.ts +55 -8
- package/src/routes/chat/errors.ts +8 -0
- package/src/routes/chat/retry-policy.ts +7 -0
- package/src/routes/chat/streaming.ts +42 -3
- package/src/routes/upload.ts +51 -12
- package/src/services/auth-playwright.ts +5 -28
- package/src/services/playwright.ts +338 -112
- package/src/services/qwen.ts +2 -2
- package/src/sync/codex.ts +154 -65
- package/src/sync/index.ts +7 -2
- package/src/tools/parser.ts +103 -0
- package/src/tools/toolcall-tags.ts +2 -2
- package/src/tui/server-manager.ts +6 -0
- package/src/tui/views/accounts-view.ts +54 -6
- package/src/tui/views/logs-view.ts +4 -20
- package/src/tui/views/status-view.ts +496 -3
- package/src/tui/views/sync-view.ts +22 -10
- package/src/utils/jwt.ts +40 -0
package/src/routes/upload.ts
CHANGED
|
@@ -341,21 +341,22 @@ async function getSTSToken(
|
|
|
341
341
|
filesize: number,
|
|
342
342
|
filetype: string,
|
|
343
343
|
headers: Record<string, string>,
|
|
344
|
+
accountId?: string,
|
|
344
345
|
): Promise<STSResponse["data"]> {
|
|
345
|
-
const
|
|
346
|
-
qwenUrl("/api/v2/files/getstsToken"),
|
|
347
|
-
{
|
|
346
|
+
const doFetch = (reqHeaders: Record<string, string>) =>
|
|
347
|
+
fetch(qwenUrl("/api/v2/files/getstsToken"), {
|
|
348
348
|
method: "POST",
|
|
349
349
|
headers: buildQwenRequestHeaders({
|
|
350
|
-
cookie:
|
|
351
|
-
userAgent:
|
|
352
|
-
bxUa:
|
|
353
|
-
bxUmidtoken:
|
|
354
|
-
bxV:
|
|
350
|
+
cookie: reqHeaders.cookie,
|
|
351
|
+
userAgent: reqHeaders["user-agent"],
|
|
352
|
+
bxUa: reqHeaders["bx-ua"],
|
|
353
|
+
bxUmidtoken: reqHeaders["bx-umidtoken"],
|
|
354
|
+
bxV: reqHeaders["bx-v"],
|
|
355
355
|
}),
|
|
356
356
|
body: JSON.stringify({ filename, filesize: String(filesize), filetype }),
|
|
357
|
-
}
|
|
358
|
-
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
let response = await doFetch(headers);
|
|
359
360
|
|
|
360
361
|
if (!response.ok) {
|
|
361
362
|
const errorText = await response.text().catch(() => "");
|
|
@@ -364,8 +365,43 @@ async function getSTSToken(
|
|
|
364
365
|
);
|
|
365
366
|
}
|
|
366
367
|
|
|
367
|
-
|
|
368
|
-
|
|
368
|
+
let data = await response.json().catch(() => null);
|
|
369
|
+
const is401 =
|
|
370
|
+
data?.success === false &&
|
|
371
|
+
(data?.data?.code === "Unauthorized" ||
|
|
372
|
+
(typeof data?.data?.details === "string" &&
|
|
373
|
+
data.data.details.includes("401")));
|
|
374
|
+
|
|
375
|
+
if (is401) {
|
|
376
|
+
try {
|
|
377
|
+
const { refreshHeaders } = await import("../services/playwright.ts");
|
|
378
|
+
const { getBasicHeaders } = await import("../services/auth-playwright.ts");
|
|
379
|
+
const { loadAccounts } = await import("../core/accounts.ts");
|
|
380
|
+
const resolvedId = accountId ?? loadAccounts()[0]?.id;
|
|
381
|
+
if (resolvedId) {
|
|
382
|
+
console.warn(
|
|
383
|
+
`[Upload] STS token 401 — refreshing session with re-auth and retrying...`,
|
|
384
|
+
);
|
|
385
|
+
await refreshHeaders(resolvedId, undefined, true);
|
|
386
|
+
const fresh = await getBasicHeaders(resolvedId);
|
|
387
|
+
headers.cookie = fresh.cookie;
|
|
388
|
+
headers["user-agent"] = fresh.userAgent;
|
|
389
|
+
headers["bx-v"] = fresh.bxV;
|
|
390
|
+
if (fresh.bxUa) headers["bx-ua"] = fresh.bxUa;
|
|
391
|
+
if (fresh.bxUmidtoken) headers["bx-umidtoken"] = fresh.bxUmidtoken;
|
|
392
|
+
response = await doFetch(headers);
|
|
393
|
+
if (response.ok) {
|
|
394
|
+
data = await response.json().catch(() => null);
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
} catch (refreshErr) {
|
|
398
|
+
console.warn(
|
|
399
|
+
`[Upload] Re-auth for STS token failed: ${refreshErr instanceof Error ? refreshErr.message : String(refreshErr)}`,
|
|
400
|
+
);
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
if (!data?.success || !data?.data) {
|
|
369
405
|
throw new Error(
|
|
370
406
|
`STS token invalid: ${JSON.stringify(data).substring(0, 200)}`,
|
|
371
407
|
);
|
|
@@ -617,6 +653,7 @@ export async function processImagesForQwen(
|
|
|
617
653
|
file_url?: { url: string };
|
|
618
654
|
}>,
|
|
619
655
|
headers: Record<string, string>,
|
|
656
|
+
accountId?: string,
|
|
620
657
|
): Promise<{ text: string; files: QwenFileEntry[] }> {
|
|
621
658
|
const textParts: string[] = [];
|
|
622
659
|
const files: QwenFileEntry[] = [];
|
|
@@ -654,6 +691,7 @@ export async function processImagesForQwen(
|
|
|
654
691
|
fileSize,
|
|
655
692
|
typeInfo.qwenFileType,
|
|
656
693
|
headers,
|
|
694
|
+
accountId,
|
|
657
695
|
);
|
|
658
696
|
fileUrl = await uploadToOSS(remoteMedia.buffer, stsData, filename);
|
|
659
697
|
fileId = stsData.file_id;
|
|
@@ -686,6 +724,7 @@ export async function processImagesForQwen(
|
|
|
686
724
|
fileSize,
|
|
687
725
|
typeInfo.qwenFileType,
|
|
688
726
|
headers,
|
|
727
|
+
accountId,
|
|
689
728
|
);
|
|
690
729
|
fileUrl = await uploadToOSS(buffer, stsData, filename);
|
|
691
730
|
fileId = stsData.file_id;
|
|
@@ -94,36 +94,13 @@ export async function getBasicHeaders(accountId?: string): Promise<{
|
|
|
94
94
|
return getPlaywrightBasicHeaders(resolvedAccountId);
|
|
95
95
|
}
|
|
96
96
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
minutesBeforeExpiry = 5,
|
|
100
|
-
): boolean {
|
|
101
|
-
const tokenMatch = cookie.match(/token=([^;]+)/);
|
|
102
|
-
if (!tokenMatch) return false;
|
|
103
|
-
|
|
104
|
-
try {
|
|
105
|
-
const token = decodeURIComponent(tokenMatch[1]);
|
|
106
|
-
const segments = token.split(".");
|
|
107
|
-
// Some Qwen deployments use opaque cookies. Treating those as expired
|
|
108
|
-
// forces expensive header capture on every personalization request.
|
|
109
|
-
if (segments.length !== 3 || !segments[1]) return false;
|
|
110
|
-
|
|
111
|
-
const payloadJson = Buffer.from(segments[1], "base64url").toString("utf-8");
|
|
112
|
-
const payload = JSON.parse(payloadJson);
|
|
113
|
-
const exp = payload.exp;
|
|
114
|
-
if (typeof exp !== "number" || !Number.isFinite(exp)) return false;
|
|
115
|
-
|
|
116
|
-
const nowSec = Math.floor(Date.now() / 1000);
|
|
117
|
-
const thresholdSec = minutesBeforeExpiry * 60;
|
|
118
|
-
return exp - nowSec < thresholdSec;
|
|
119
|
-
} catch {
|
|
120
|
-
return false;
|
|
121
|
-
}
|
|
122
|
-
}
|
|
97
|
+
import { parseJwtExpiry, isTokenExpiringSoon } from "../utils/jwt.ts";
|
|
98
|
+
export { parseJwtExpiry, isTokenExpiringSoon };
|
|
123
99
|
|
|
124
100
|
export async function getQwenHeaders(
|
|
125
101
|
forceNew = false,
|
|
126
102
|
accountId?: string,
|
|
103
|
+
forceReauth = false,
|
|
127
104
|
): Promise<HeaderResult> {
|
|
128
105
|
if (isAuthMockEnabled()) {
|
|
129
106
|
const basic = await getBasicHeaders(accountId);
|
|
@@ -150,8 +127,8 @@ export async function getQwenHeaders(
|
|
|
150
127
|
|
|
151
128
|
await ensurePlaywrightInitialized(resolvedAccountId);
|
|
152
129
|
|
|
153
|
-
if (forceNew) {
|
|
154
|
-
await refreshHeaders(resolvedAccountId);
|
|
130
|
+
if (forceNew || forceReauth) {
|
|
131
|
+
await refreshHeaders(resolvedAccountId, config.timeouts.headers, forceReauth);
|
|
155
132
|
}
|
|
156
133
|
|
|
157
134
|
const basic = await getPlaywrightBasicHeaders(resolvedAccountId);
|