qwenproxy-cli 1.5.0 → 1.5.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/dist/clean-cache.js +38 -44
- package/dist/delete-chats.js +38 -44
- package/dist/index.js +65 -70
- package/dist/login.js +35 -44
- package/dist/reset-cooldowns.js +38 -44
- package/package.json +3 -3
- package/src/core/version.ts +1 -1
- package/src/routes/chat/streaming.ts +1 -1
- package/src/services/playwright.ts +40 -49
- package/src/tools/instructions.ts +38 -25
- package/src/tools/parser.ts +7 -0
package/dist/clean-cache.js
CHANGED
|
@@ -1967,6 +1967,9 @@ function unmarkAccountHeadersReady(accountId) {
|
|
|
1967
1967
|
if (!accountId) return;
|
|
1968
1968
|
headersReadyAccounts.delete(accountId);
|
|
1969
1969
|
}
|
|
1970
|
+
function isAccountHeadersReady(accountId) {
|
|
1971
|
+
return headersReadyAccounts.has(accountId);
|
|
1972
|
+
}
|
|
1970
1973
|
var cooldowns, headersReadyAccounts;
|
|
1971
1974
|
var init_account_manager = __esm({
|
|
1972
1975
|
"src/core/account-manager.ts"() {
|
|
@@ -2898,26 +2901,6 @@ async function isPageLoggedIn(page, timeoutMs = SESSION_PROBE_NAVIGATION_TIMEOUT
|
|
|
2898
2901
|
}
|
|
2899
2902
|
} catch {
|
|
2900
2903
|
}
|
|
2901
|
-
try {
|
|
2902
|
-
const refreshRes = await fetch("https://auth.qwen.ai/api/v2/auths/refresh", {
|
|
2903
|
-
method: "GET",
|
|
2904
|
-
credentials: "include",
|
|
2905
|
-
signal: AbortSignal.timeout(3e3)
|
|
2906
|
-
});
|
|
2907
|
-
if (refreshRes.status === 200) {
|
|
2908
|
-
const refreshJson = await refreshRes.json().catch(() => null);
|
|
2909
|
-
if (refreshJson && refreshJson.success === false) {
|
|
2910
|
-
const code = refreshJson.data?.code || refreshJson.code;
|
|
2911
|
-
const details = String(refreshJson.data?.details || "");
|
|
2912
|
-
if (code === "Unauthorized" || details.includes("revogado") || details.includes("revoked")) {
|
|
2913
|
-
return false;
|
|
2914
|
-
}
|
|
2915
|
-
}
|
|
2916
|
-
} else if (refreshRes.status === 401 || refreshRes.status === 403) {
|
|
2917
|
-
return false;
|
|
2918
|
-
}
|
|
2919
|
-
} catch {
|
|
2920
|
-
}
|
|
2921
2904
|
return true;
|
|
2922
2905
|
} catch {
|
|
2923
2906
|
return false;
|
|
@@ -3723,7 +3706,7 @@ async function initPlaywrightForAccount(rawAccount, headless = config.playwright
|
|
|
3723
3706
|
`\u26A0\uFE0F [Playwright] Initial navigation check for ${maskEmail(account.email)}: ${err.message}`
|
|
3724
3707
|
);
|
|
3725
3708
|
}
|
|
3726
|
-
if (!options.skipHeaderCapture) {
|
|
3709
|
+
if (!options.skipHeaderCapture && !isAccountHeadersReady(account.id)) {
|
|
3727
3710
|
acctPage.__qwenChatHomeLoaded = true;
|
|
3728
3711
|
await captureQwenHeaders(account.id);
|
|
3729
3712
|
}
|
|
@@ -3933,7 +3916,7 @@ async function loginToQwen(accountId, email, password) {
|
|
|
3933
3916
|
);
|
|
3934
3917
|
markAccountRateLimited(
|
|
3935
3918
|
accountId,
|
|
3936
|
-
|
|
3919
|
+
300 * 1e3,
|
|
3937
3920
|
"AuthFailed: All login methods exhausted"
|
|
3938
3921
|
);
|
|
3939
3922
|
return false;
|
|
@@ -4210,38 +4193,49 @@ async function loginViaUi(page, email, password) {
|
|
|
4210
4193
|
try {
|
|
4211
4194
|
const emailEl = document.querySelector('input[name="email"], input[type="email"], input[type="text"]');
|
|
4212
4195
|
const passEl = document.querySelector('input[type="password"], input[name="password"]');
|
|
4213
|
-
|
|
4214
|
-
|
|
4215
|
-
|
|
4216
|
-
|
|
4217
|
-
|
|
4218
|
-
|
|
4219
|
-
|
|
4220
|
-
|
|
4221
|
-
|
|
4222
|
-
|
|
4196
|
+
const setReactValue = (el, val) => {
|
|
4197
|
+
const proto = Object.getPrototypeOf(el);
|
|
4198
|
+
const setter = Object.getOwnPropertyDescriptor(proto, "value")?.set;
|
|
4199
|
+
if (setter) {
|
|
4200
|
+
setter.call(el, val);
|
|
4201
|
+
} else {
|
|
4202
|
+
el.value = val;
|
|
4203
|
+
}
|
|
4204
|
+
el.dispatchEvent(new Event("input", { bubbles: true }));
|
|
4205
|
+
el.dispatchEvent(new Event("change", { bubbles: true }));
|
|
4206
|
+
el.dispatchEvent(new Event("blur", { bubbles: true }));
|
|
4207
|
+
};
|
|
4208
|
+
if (emailEl) setReactValue(emailEl, emailEl.value);
|
|
4209
|
+
if (passEl) setReactValue(passEl, passEl.value);
|
|
4223
4210
|
} catch {
|
|
4224
4211
|
}
|
|
4225
4212
|
}).catch(() => {
|
|
4226
4213
|
});
|
|
4214
|
+
await sleep2(500);
|
|
4227
4215
|
const submitSelector = 'button.qwenchat-auth-pc-submit-button, button[type="submit"], button:has-text("Log in"), button:has-text("Sign in"), button:has-text("Fazer login"), button:has-text("Entrar"), button:has-text("Iniciar sesi\xF3n"), button:has-text("\u767B\u5F55")';
|
|
4228
4216
|
const submitButton = page.locator(submitSelector).first();
|
|
4217
|
+
await page.evaluate((sel) => {
|
|
4218
|
+
try {
|
|
4219
|
+
const btn = document.querySelector(sel);
|
|
4220
|
+
if (btn) {
|
|
4221
|
+
btn.removeAttribute("disabled");
|
|
4222
|
+
btn.classList.remove("ant-btn-disabled");
|
|
4223
|
+
btn.click();
|
|
4224
|
+
}
|
|
4225
|
+
} catch {
|
|
4226
|
+
}
|
|
4227
|
+
}, submitSelector).catch(() => {
|
|
4228
|
+
});
|
|
4229
4229
|
try {
|
|
4230
|
-
await
|
|
4231
|
-
timeout:
|
|
4232
|
-
});
|
|
4233
|
-
await submitButton.click();
|
|
4234
|
-
} catch {
|
|
4235
|
-
const isDisabled = await submitButton.isDisabled().catch(() => true);
|
|
4236
|
-
if (!isDisabled) {
|
|
4237
|
-
await submitButton.click({ force: true }).catch(() => {
|
|
4230
|
+
if (await submitButton.isVisible({ timeout: 2e3 }).catch(() => false)) {
|
|
4231
|
+
await submitButton.click({ force: true, timeout: 2e3 }).catch(() => {
|
|
4238
4232
|
});
|
|
4239
|
-
} else {
|
|
4240
|
-
if (!page.url().includes("/auth") && await isPageLoggedIn(page, 2e3)) {
|
|
4241
|
-
return { success: true };
|
|
4242
|
-
}
|
|
4243
4233
|
}
|
|
4234
|
+
} catch {
|
|
4244
4235
|
}
|
|
4236
|
+
const passInput = page.locator(passwordSelector).first();
|
|
4237
|
+
await passInput.press("Enter", { timeout: 2e3 }).catch(() => {
|
|
4238
|
+
});
|
|
4245
4239
|
const postSubmitDeadline = Date.now() + 15e3;
|
|
4246
4240
|
while (Date.now() < postSubmitDeadline) {
|
|
4247
4241
|
if (page.isClosed()) break;
|
package/dist/delete-chats.js
CHANGED
|
@@ -2030,6 +2030,9 @@ function unmarkAccountHeadersReady(accountId) {
|
|
|
2030
2030
|
if (!accountId) return;
|
|
2031
2031
|
headersReadyAccounts.delete(accountId);
|
|
2032
2032
|
}
|
|
2033
|
+
function isAccountHeadersReady(accountId) {
|
|
2034
|
+
return headersReadyAccounts.has(accountId);
|
|
2035
|
+
}
|
|
2033
2036
|
var cooldowns, headersReadyAccounts;
|
|
2034
2037
|
var init_account_manager = __esm({
|
|
2035
2038
|
"src/core/account-manager.ts"() {
|
|
@@ -3333,26 +3336,6 @@ async function isPageLoggedIn(page, timeoutMs = SESSION_PROBE_NAVIGATION_TIMEOUT
|
|
|
3333
3336
|
}
|
|
3334
3337
|
} catch {
|
|
3335
3338
|
}
|
|
3336
|
-
try {
|
|
3337
|
-
const refreshRes = await fetch("https://auth.qwen.ai/api/v2/auths/refresh", {
|
|
3338
|
-
method: "GET",
|
|
3339
|
-
credentials: "include",
|
|
3340
|
-
signal: AbortSignal.timeout(3e3)
|
|
3341
|
-
});
|
|
3342
|
-
if (refreshRes.status === 200) {
|
|
3343
|
-
const refreshJson = await refreshRes.json().catch(() => null);
|
|
3344
|
-
if (refreshJson && refreshJson.success === false) {
|
|
3345
|
-
const code = refreshJson.data?.code || refreshJson.code;
|
|
3346
|
-
const details = String(refreshJson.data?.details || "");
|
|
3347
|
-
if (code === "Unauthorized" || details.includes("revogado") || details.includes("revoked")) {
|
|
3348
|
-
return false;
|
|
3349
|
-
}
|
|
3350
|
-
}
|
|
3351
|
-
} else if (refreshRes.status === 401 || refreshRes.status === 403) {
|
|
3352
|
-
return false;
|
|
3353
|
-
}
|
|
3354
|
-
} catch {
|
|
3355
|
-
}
|
|
3356
3339
|
return true;
|
|
3357
3340
|
} catch {
|
|
3358
3341
|
return false;
|
|
@@ -4480,7 +4463,7 @@ async function initPlaywrightForAccount(rawAccount, headless = config.playwright
|
|
|
4480
4463
|
`\u26A0\uFE0F [Playwright] Initial navigation check for ${maskEmail(account.email)}: ${err.message}`
|
|
4481
4464
|
);
|
|
4482
4465
|
}
|
|
4483
|
-
if (!options.skipHeaderCapture) {
|
|
4466
|
+
if (!options.skipHeaderCapture && !isAccountHeadersReady(account.id)) {
|
|
4484
4467
|
acctPage.__qwenChatHomeLoaded = true;
|
|
4485
4468
|
await captureQwenHeaders(account.id);
|
|
4486
4469
|
}
|
|
@@ -4810,7 +4793,7 @@ async function loginToQwen(accountId, email, password) {
|
|
|
4810
4793
|
);
|
|
4811
4794
|
markAccountRateLimited(
|
|
4812
4795
|
accountId,
|
|
4813
|
-
|
|
4796
|
+
300 * 1e3,
|
|
4814
4797
|
"AuthFailed: All login methods exhausted"
|
|
4815
4798
|
);
|
|
4816
4799
|
return false;
|
|
@@ -5087,38 +5070,49 @@ async function loginViaUi(page, email, password) {
|
|
|
5087
5070
|
try {
|
|
5088
5071
|
const emailEl = document.querySelector('input[name="email"], input[type="email"], input[type="text"]');
|
|
5089
5072
|
const passEl = document.querySelector('input[type="password"], input[name="password"]');
|
|
5090
|
-
|
|
5091
|
-
|
|
5092
|
-
|
|
5093
|
-
|
|
5094
|
-
|
|
5095
|
-
|
|
5096
|
-
|
|
5097
|
-
|
|
5098
|
-
|
|
5099
|
-
|
|
5073
|
+
const setReactValue = (el, val) => {
|
|
5074
|
+
const proto = Object.getPrototypeOf(el);
|
|
5075
|
+
const setter = Object.getOwnPropertyDescriptor(proto, "value")?.set;
|
|
5076
|
+
if (setter) {
|
|
5077
|
+
setter.call(el, val);
|
|
5078
|
+
} else {
|
|
5079
|
+
el.value = val;
|
|
5080
|
+
}
|
|
5081
|
+
el.dispatchEvent(new Event("input", { bubbles: true }));
|
|
5082
|
+
el.dispatchEvent(new Event("change", { bubbles: true }));
|
|
5083
|
+
el.dispatchEvent(new Event("blur", { bubbles: true }));
|
|
5084
|
+
};
|
|
5085
|
+
if (emailEl) setReactValue(emailEl, emailEl.value);
|
|
5086
|
+
if (passEl) setReactValue(passEl, passEl.value);
|
|
5100
5087
|
} catch {
|
|
5101
5088
|
}
|
|
5102
5089
|
}).catch(() => {
|
|
5103
5090
|
});
|
|
5091
|
+
await sleep2(500);
|
|
5104
5092
|
const submitSelector = 'button.qwenchat-auth-pc-submit-button, button[type="submit"], button:has-text("Log in"), button:has-text("Sign in"), button:has-text("Fazer login"), button:has-text("Entrar"), button:has-text("Iniciar sesi\xF3n"), button:has-text("\u767B\u5F55")';
|
|
5105
5093
|
const submitButton = page.locator(submitSelector).first();
|
|
5094
|
+
await page.evaluate((sel) => {
|
|
5095
|
+
try {
|
|
5096
|
+
const btn = document.querySelector(sel);
|
|
5097
|
+
if (btn) {
|
|
5098
|
+
btn.removeAttribute("disabled");
|
|
5099
|
+
btn.classList.remove("ant-btn-disabled");
|
|
5100
|
+
btn.click();
|
|
5101
|
+
}
|
|
5102
|
+
} catch {
|
|
5103
|
+
}
|
|
5104
|
+
}, submitSelector).catch(() => {
|
|
5105
|
+
});
|
|
5106
5106
|
try {
|
|
5107
|
-
await
|
|
5108
|
-
timeout:
|
|
5109
|
-
});
|
|
5110
|
-
await submitButton.click();
|
|
5111
|
-
} catch {
|
|
5112
|
-
const isDisabled = await submitButton.isDisabled().catch(() => true);
|
|
5113
|
-
if (!isDisabled) {
|
|
5114
|
-
await submitButton.click({ force: true }).catch(() => {
|
|
5107
|
+
if (await submitButton.isVisible({ timeout: 2e3 }).catch(() => false)) {
|
|
5108
|
+
await submitButton.click({ force: true, timeout: 2e3 }).catch(() => {
|
|
5115
5109
|
});
|
|
5116
|
-
} else {
|
|
5117
|
-
if (!page.url().includes("/auth") && await isPageLoggedIn(page, 2e3)) {
|
|
5118
|
-
return { success: true };
|
|
5119
|
-
}
|
|
5120
5110
|
}
|
|
5111
|
+
} catch {
|
|
5121
5112
|
}
|
|
5113
|
+
const passInput = page.locator(passwordSelector).first();
|
|
5114
|
+
await passInput.press("Enter", { timeout: 2e3 }).catch(() => {
|
|
5115
|
+
});
|
|
5122
5116
|
const postSubmitDeadline = Date.now() + 15e3;
|
|
5123
5117
|
while (Date.now() < postSubmitDeadline) {
|
|
5124
5118
|
if (page.isClosed()) break;
|
package/dist/index.js
CHANGED
|
@@ -4537,26 +4537,6 @@ async function isPageLoggedIn(page, timeoutMs = SESSION_PROBE_NAVIGATION_TIMEOUT
|
|
|
4537
4537
|
}
|
|
4538
4538
|
} catch {
|
|
4539
4539
|
}
|
|
4540
|
-
try {
|
|
4541
|
-
const refreshRes = await fetch("https://auth.qwen.ai/api/v2/auths/refresh", {
|
|
4542
|
-
method: "GET",
|
|
4543
|
-
credentials: "include",
|
|
4544
|
-
signal: AbortSignal.timeout(3e3)
|
|
4545
|
-
});
|
|
4546
|
-
if (refreshRes.status === 200) {
|
|
4547
|
-
const refreshJson = await refreshRes.json().catch(() => null);
|
|
4548
|
-
if (refreshJson && refreshJson.success === false) {
|
|
4549
|
-
const code = refreshJson.data?.code || refreshJson.code;
|
|
4550
|
-
const details = String(refreshJson.data?.details || "");
|
|
4551
|
-
if (code === "Unauthorized" || details.includes("revogado") || details.includes("revoked")) {
|
|
4552
|
-
return false;
|
|
4553
|
-
}
|
|
4554
|
-
}
|
|
4555
|
-
} else if (refreshRes.status === 401 || refreshRes.status === 403) {
|
|
4556
|
-
return false;
|
|
4557
|
-
}
|
|
4558
|
-
} catch {
|
|
4559
|
-
}
|
|
4560
4540
|
return true;
|
|
4561
4541
|
} catch {
|
|
4562
4542
|
return false;
|
|
@@ -5684,7 +5664,7 @@ async function initPlaywrightForAccount(rawAccount, headless = config.playwright
|
|
|
5684
5664
|
`\u26A0\uFE0F [Playwright] Initial navigation check for ${maskEmail(account.email)}: ${err.message}`
|
|
5685
5665
|
);
|
|
5686
5666
|
}
|
|
5687
|
-
if (!options.skipHeaderCapture) {
|
|
5667
|
+
if (!options.skipHeaderCapture && !isAccountHeadersReady(account.id)) {
|
|
5688
5668
|
acctPage.__qwenChatHomeLoaded = true;
|
|
5689
5669
|
await captureQwenHeaders(account.id);
|
|
5690
5670
|
}
|
|
@@ -6014,7 +5994,7 @@ async function loginToQwen(accountId, email, password) {
|
|
|
6014
5994
|
);
|
|
6015
5995
|
markAccountRateLimited(
|
|
6016
5996
|
accountId,
|
|
6017
|
-
|
|
5997
|
+
300 * 1e3,
|
|
6018
5998
|
"AuthFailed: All login methods exhausted"
|
|
6019
5999
|
);
|
|
6020
6000
|
return false;
|
|
@@ -6291,38 +6271,49 @@ async function loginViaUi(page, email, password) {
|
|
|
6291
6271
|
try {
|
|
6292
6272
|
const emailEl = document.querySelector('input[name="email"], input[type="email"], input[type="text"]');
|
|
6293
6273
|
const passEl = document.querySelector('input[type="password"], input[name="password"]');
|
|
6294
|
-
|
|
6295
|
-
|
|
6296
|
-
|
|
6297
|
-
|
|
6298
|
-
|
|
6299
|
-
|
|
6300
|
-
|
|
6301
|
-
|
|
6302
|
-
|
|
6303
|
-
|
|
6274
|
+
const setReactValue = (el, val) => {
|
|
6275
|
+
const proto = Object.getPrototypeOf(el);
|
|
6276
|
+
const setter = Object.getOwnPropertyDescriptor(proto, "value")?.set;
|
|
6277
|
+
if (setter) {
|
|
6278
|
+
setter.call(el, val);
|
|
6279
|
+
} else {
|
|
6280
|
+
el.value = val;
|
|
6281
|
+
}
|
|
6282
|
+
el.dispatchEvent(new Event("input", { bubbles: true }));
|
|
6283
|
+
el.dispatchEvent(new Event("change", { bubbles: true }));
|
|
6284
|
+
el.dispatchEvent(new Event("blur", { bubbles: true }));
|
|
6285
|
+
};
|
|
6286
|
+
if (emailEl) setReactValue(emailEl, emailEl.value);
|
|
6287
|
+
if (passEl) setReactValue(passEl, passEl.value);
|
|
6304
6288
|
} catch {
|
|
6305
6289
|
}
|
|
6306
6290
|
}).catch(() => {
|
|
6307
6291
|
});
|
|
6292
|
+
await sleep2(500);
|
|
6308
6293
|
const submitSelector = 'button.qwenchat-auth-pc-submit-button, button[type="submit"], button:has-text("Log in"), button:has-text("Sign in"), button:has-text("Fazer login"), button:has-text("Entrar"), button:has-text("Iniciar sesi\xF3n"), button:has-text("\u767B\u5F55")';
|
|
6309
6294
|
const submitButton = page.locator(submitSelector).first();
|
|
6295
|
+
await page.evaluate((sel) => {
|
|
6296
|
+
try {
|
|
6297
|
+
const btn = document.querySelector(sel);
|
|
6298
|
+
if (btn) {
|
|
6299
|
+
btn.removeAttribute("disabled");
|
|
6300
|
+
btn.classList.remove("ant-btn-disabled");
|
|
6301
|
+
btn.click();
|
|
6302
|
+
}
|
|
6303
|
+
} catch {
|
|
6304
|
+
}
|
|
6305
|
+
}, submitSelector).catch(() => {
|
|
6306
|
+
});
|
|
6310
6307
|
try {
|
|
6311
|
-
await
|
|
6312
|
-
timeout:
|
|
6313
|
-
});
|
|
6314
|
-
await submitButton.click();
|
|
6315
|
-
} catch {
|
|
6316
|
-
const isDisabled = await submitButton.isDisabled().catch(() => true);
|
|
6317
|
-
if (!isDisabled) {
|
|
6318
|
-
await submitButton.click({ force: true }).catch(() => {
|
|
6308
|
+
if (await submitButton.isVisible({ timeout: 2e3 }).catch(() => false)) {
|
|
6309
|
+
await submitButton.click({ force: true, timeout: 2e3 }).catch(() => {
|
|
6319
6310
|
});
|
|
6320
|
-
} else {
|
|
6321
|
-
if (!page.url().includes("/auth") && await isPageLoggedIn(page, 2e3)) {
|
|
6322
|
-
return { success: true };
|
|
6323
|
-
}
|
|
6324
6311
|
}
|
|
6312
|
+
} catch {
|
|
6325
6313
|
}
|
|
6314
|
+
const passInput = page.locator(passwordSelector).first();
|
|
6315
|
+
await passInput.press("Enter", { timeout: 2e3 }).catch(() => {
|
|
6316
|
+
});
|
|
6326
6317
|
const postSubmitDeadline = Date.now() + 15e3;
|
|
6327
6318
|
while (Date.now() < postSubmitDeadline) {
|
|
6328
6319
|
if (page.isClosed()) break;
|
|
@@ -14699,12 +14690,12 @@ function buildToolInstructions(toolsJson, toolChoice) {
|
|
|
14699
14690
|
const sampleToolName = typeof firstTool?.function?.name === "string" && firstTool.function.name || typeof firstTool?.name === "string" && firstTool.name || "tool_name";
|
|
14700
14691
|
const properties = firstTool?.function?.parameters?.properties || firstTool?.parameters?.properties;
|
|
14701
14692
|
const firstPropKey = properties && typeof properties === "object" ? Object.keys(properties)[0] : null;
|
|
14702
|
-
|
|
14703
|
-
|
|
14704
|
-
|
|
14705
|
-
|
|
14706
|
-
|
|
14707
|
-
}
|
|
14693
|
+
const sampleArgs1 = firstPropKey ? JSON.stringify({ [firstPropKey]: "value1" }) : "{}";
|
|
14694
|
+
const secondTool = toolList.length > 1 ? toolList[1] : null;
|
|
14695
|
+
const secondToolName = secondTool ? typeof secondTool?.function?.name === "string" && secondTool.function.name || typeof secondTool?.name === "string" && secondTool.name || sampleToolName : null;
|
|
14696
|
+
const secondProperties = secondTool?.function?.parameters?.properties || secondTool?.parameters?.properties;
|
|
14697
|
+
const secondPropKey = secondProperties && typeof secondProperties === "object" ? Object.keys(secondProperties)[0] : null;
|
|
14698
|
+
const sampleArgs2 = secondPropKey ? JSON.stringify({ [secondPropKey]: "value2" }) : "{}";
|
|
14708
14699
|
let forcedInstruction = "";
|
|
14709
14700
|
if (toolChoice && typeof toolChoice === "object" && toolChoice.function?.name) {
|
|
14710
14701
|
forcedInstruction = `
|
|
@@ -14715,32 +14706,30 @@ CRITICAL: You MUST call the tool "${toolChoice.function.name}" in this response.
|
|
|
14715
14706
|
CRITICAL: You MUST call at least one tool from the list above in this response.
|
|
14716
14707
|
`;
|
|
14717
14708
|
}
|
|
14709
|
+
const secondExample = secondToolName ? `
|
|
14710
|
+
${TOOL_CALL_OPEN}
|
|
14711
|
+
{"name": "${secondToolName}", "arguments": ${sampleArgs2}}
|
|
14712
|
+
${TOOL_CALL_CLOSE}` : "";
|
|
14718
14713
|
let instructions = `
|
|
14719
14714
|
|
|
14720
14715
|
# TOOLS AVAILABLE
|
|
14716
|
+
You may call one or more functions to assist with the user query.
|
|
14717
|
+
You are provided with function signatures within <tools></tools> XML tags:
|
|
14718
|
+
<tools>
|
|
14721
14719
|
${manifest}
|
|
14720
|
+
</tools>
|
|
14722
14721
|
${forcedInstruction}
|
|
14723
|
-
[TOOL CALL CONTRACT
|
|
14724
|
-
To
|
|
14725
|
-
When calling multiple independent tools, output consecutive blocks:
|
|
14726
|
-
|
|
14722
|
+
[TOOL CALL CONTRACT]
|
|
14723
|
+
To call a tool, output a JSON object wrapped EXACTLY in ${TOOL_CALL_OPEN} and ${TOOL_CALL_CLOSE} tags:
|
|
14727
14724
|
${TOOL_CALL_OPEN}
|
|
14728
14725
|
{"name": "${sampleToolName}", "arguments": ${sampleArgs1}}
|
|
14729
|
-
${TOOL_CALL_CLOSE}
|
|
14730
|
-
${TOOL_CALL_OPEN}
|
|
14731
|
-
{"name": "${sampleToolName}", "arguments": ${sampleArgs2}}
|
|
14732
|
-
${TOOL_CALL_CLOSE}
|
|
14726
|
+
${TOOL_CALL_CLOSE}${secondExample}
|
|
14733
14727
|
|
|
14734
|
-
|
|
14735
|
-
1.
|
|
14736
|
-
2.
|
|
14737
|
-
3.
|
|
14738
|
-
4.
|
|
14739
|
-
5. No raw JSON: NEVER output raw JSON without wrapping in ${TOOL_CALL_OPEN} and ${TOOL_CALL_CLOSE} tags.
|
|
14740
|
-
6. Clean blocks: Put only valid JSON inside each block \u2014 no markdown fences (\`\`\`json), comments, or explanatory text.
|
|
14741
|
-
7. Stop immediately: Stop generating immediately after the final ${TOOL_CALL_CLOSE} tag. Do not emit trailing dots, ellipsis (......), explanations, or reasoning after the tool calls.
|
|
14742
|
-
8. Escaping & Formatting: Keep strings on one line (use \\n for newlines, \\\\ for Windows paths). Do not split values across lines.
|
|
14743
|
-
9. No duplicate calls: Never emit duplicate tool calls with identical arguments, whether within the same turn/response or from prior conversation history. Every call must perform distinct work.
|
|
14728
|
+
GUIDELINES:
|
|
14729
|
+
1. Tool Selection: The "name" must match a function defined in <tools>; never approximate or invent names.
|
|
14730
|
+
2. Arguments Format: "arguments" must be a valid JSON object matching the function's parameter schema. Put only valid JSON inside each block \u2014 no markdown fences (\`\`\`json), comments, or text.
|
|
14731
|
+
3. Parallel Execution: When multiple operations are needed, emit consecutive ${TOOL_CALL_OPEN} blocks (at most 4 per turn). Each block must be complete and self-contained (never nested, interleaved, or omitted).
|
|
14732
|
+
4. Direct Invocation: Stop generation after the tool call block so the tools can be executed. Never output raw JSON without ${TOOL_CALL_OPEN} tags.
|
|
14744
14733
|
`;
|
|
14745
14734
|
if (toolInstructionsCache.size >= TOOL_CACHE_MAX_ENTRIES) {
|
|
14746
14735
|
toolInstructionsCache.clear();
|
|
@@ -18215,6 +18204,7 @@ var init_parser = __esm({
|
|
|
18215
18204
|
currentOpenTag = TOOL_START_LITERAL;
|
|
18216
18205
|
currentCloseTag = TOOL_END;
|
|
18217
18206
|
emittedToolCallCount = 0;
|
|
18207
|
+
successfulToolCallsCount = 0;
|
|
18218
18208
|
pendingLeadIn = "";
|
|
18219
18209
|
tools = [];
|
|
18220
18210
|
declaredToolNames = [];
|
|
@@ -18422,6 +18412,7 @@ var init_parser = __esm({
|
|
|
18422
18412
|
if (matchesIncrementalCall) {
|
|
18423
18413
|
this.flushPendingToolCallDeltas(result);
|
|
18424
18414
|
this.emittedToolCallCount++;
|
|
18415
|
+
this.successfulToolCallsCount++;
|
|
18425
18416
|
this.pendingLeadIn = "";
|
|
18426
18417
|
incremental.startEmitted = false;
|
|
18427
18418
|
incremental.disabled = true;
|
|
@@ -18430,6 +18421,7 @@ var init_parser = __esm({
|
|
|
18430
18421
|
this.flushPendingToolCallDeltas(result);
|
|
18431
18422
|
result.toolCalls.push(tc);
|
|
18432
18423
|
this.emittedToolCallCount++;
|
|
18424
|
+
this.successfulToolCallsCount++;
|
|
18433
18425
|
this.pendingLeadIn = "";
|
|
18434
18426
|
}
|
|
18435
18427
|
tryRecoverIncrementalToolCall(content) {
|
|
@@ -18889,6 +18881,9 @@ var init_parser = __esm({
|
|
|
18889
18881
|
getEmittedToolCallCount() {
|
|
18890
18882
|
return this.emittedToolCallCount;
|
|
18891
18883
|
}
|
|
18884
|
+
getSuccessfulToolCallCount() {
|
|
18885
|
+
return this.successfulToolCallsCount;
|
|
18886
|
+
}
|
|
18892
18887
|
isInsideTool() {
|
|
18893
18888
|
return this.insideTool;
|
|
18894
18889
|
}
|
|
@@ -21354,7 +21349,7 @@ async function processStreamingResponse(params) {
|
|
|
21354
21349
|
let activeRetryStream = null;
|
|
21355
21350
|
while (malformedRetryCount < maxMalformedRetries) {
|
|
21356
21351
|
if (!toolParser) break;
|
|
21357
|
-
const allToolsFailed = toolParser.getMalformedToolCalls().length > 0 && toolParser.
|
|
21352
|
+
const allToolsFailed = toolParser.getMalformedToolCalls().length > 0 && toolParser.getSuccessfulToolCallCount() === 0;
|
|
21358
21353
|
if (!allToolsFailed) break;
|
|
21359
21354
|
const malformedCalls = toolParser.getMalformedToolCalls();
|
|
21360
21355
|
const malformedCount = malformedCalls.length;
|
|
@@ -25889,7 +25884,7 @@ function getAppVersion() {
|
|
|
25889
25884
|
}
|
|
25890
25885
|
} catch {
|
|
25891
25886
|
}
|
|
25892
|
-
cachedVersion = "v1.5.
|
|
25887
|
+
cachedVersion = "v1.5.1";
|
|
25893
25888
|
return cachedVersion;
|
|
25894
25889
|
}
|
|
25895
25890
|
var cachedVersion;
|
package/dist/login.js
CHANGED
|
@@ -3404,26 +3404,6 @@ async function isPageLoggedIn(page, timeoutMs = SESSION_PROBE_NAVIGATION_TIMEOUT
|
|
|
3404
3404
|
}
|
|
3405
3405
|
} catch {
|
|
3406
3406
|
}
|
|
3407
|
-
try {
|
|
3408
|
-
const refreshRes = await fetch("https://auth.qwen.ai/api/v2/auths/refresh", {
|
|
3409
|
-
method: "GET",
|
|
3410
|
-
credentials: "include",
|
|
3411
|
-
signal: AbortSignal.timeout(3e3)
|
|
3412
|
-
});
|
|
3413
|
-
if (refreshRes.status === 200) {
|
|
3414
|
-
const refreshJson = await refreshRes.json().catch(() => null);
|
|
3415
|
-
if (refreshJson && refreshJson.success === false) {
|
|
3416
|
-
const code = refreshJson.data?.code || refreshJson.code;
|
|
3417
|
-
const details = String(refreshJson.data?.details || "");
|
|
3418
|
-
if (code === "Unauthorized" || details.includes("revogado") || details.includes("revoked")) {
|
|
3419
|
-
return false;
|
|
3420
|
-
}
|
|
3421
|
-
}
|
|
3422
|
-
} else if (refreshRes.status === 401 || refreshRes.status === 403) {
|
|
3423
|
-
return false;
|
|
3424
|
-
}
|
|
3425
|
-
} catch {
|
|
3426
|
-
}
|
|
3427
3407
|
return true;
|
|
3428
3408
|
} catch {
|
|
3429
3409
|
return false;
|
|
@@ -4551,7 +4531,7 @@ async function initPlaywrightForAccount(rawAccount, headless = config.playwright
|
|
|
4551
4531
|
`\u26A0\uFE0F [Playwright] Initial navigation check for ${maskEmail(account.email)}: ${err.message}`
|
|
4552
4532
|
);
|
|
4553
4533
|
}
|
|
4554
|
-
if (!options.skipHeaderCapture) {
|
|
4534
|
+
if (!options.skipHeaderCapture && !isAccountHeadersReady(account.id)) {
|
|
4555
4535
|
acctPage.__qwenChatHomeLoaded = true;
|
|
4556
4536
|
await captureQwenHeaders(account.id);
|
|
4557
4537
|
}
|
|
@@ -4881,7 +4861,7 @@ async function loginToQwen(accountId, email, password) {
|
|
|
4881
4861
|
);
|
|
4882
4862
|
markAccountRateLimited(
|
|
4883
4863
|
accountId,
|
|
4884
|
-
|
|
4864
|
+
300 * 1e3,
|
|
4885
4865
|
"AuthFailed: All login methods exhausted"
|
|
4886
4866
|
);
|
|
4887
4867
|
return false;
|
|
@@ -5158,38 +5138,49 @@ async function loginViaUi(page, email, password) {
|
|
|
5158
5138
|
try {
|
|
5159
5139
|
const emailEl = document.querySelector('input[name="email"], input[type="email"], input[type="text"]');
|
|
5160
5140
|
const passEl = document.querySelector('input[type="password"], input[name="password"]');
|
|
5161
|
-
|
|
5162
|
-
|
|
5163
|
-
|
|
5164
|
-
|
|
5165
|
-
|
|
5166
|
-
|
|
5167
|
-
|
|
5168
|
-
|
|
5169
|
-
|
|
5170
|
-
|
|
5141
|
+
const setReactValue = (el, val) => {
|
|
5142
|
+
const proto = Object.getPrototypeOf(el);
|
|
5143
|
+
const setter = Object.getOwnPropertyDescriptor(proto, "value")?.set;
|
|
5144
|
+
if (setter) {
|
|
5145
|
+
setter.call(el, val);
|
|
5146
|
+
} else {
|
|
5147
|
+
el.value = val;
|
|
5148
|
+
}
|
|
5149
|
+
el.dispatchEvent(new Event("input", { bubbles: true }));
|
|
5150
|
+
el.dispatchEvent(new Event("change", { bubbles: true }));
|
|
5151
|
+
el.dispatchEvent(new Event("blur", { bubbles: true }));
|
|
5152
|
+
};
|
|
5153
|
+
if (emailEl) setReactValue(emailEl, emailEl.value);
|
|
5154
|
+
if (passEl) setReactValue(passEl, passEl.value);
|
|
5171
5155
|
} catch {
|
|
5172
5156
|
}
|
|
5173
5157
|
}).catch(() => {
|
|
5174
5158
|
});
|
|
5159
|
+
await sleep2(500);
|
|
5175
5160
|
const submitSelector = 'button.qwenchat-auth-pc-submit-button, button[type="submit"], button:has-text("Log in"), button:has-text("Sign in"), button:has-text("Fazer login"), button:has-text("Entrar"), button:has-text("Iniciar sesi\xF3n"), button:has-text("\u767B\u5F55")';
|
|
5176
5161
|
const submitButton = page.locator(submitSelector).first();
|
|
5162
|
+
await page.evaluate((sel) => {
|
|
5163
|
+
try {
|
|
5164
|
+
const btn = document.querySelector(sel);
|
|
5165
|
+
if (btn) {
|
|
5166
|
+
btn.removeAttribute("disabled");
|
|
5167
|
+
btn.classList.remove("ant-btn-disabled");
|
|
5168
|
+
btn.click();
|
|
5169
|
+
}
|
|
5170
|
+
} catch {
|
|
5171
|
+
}
|
|
5172
|
+
}, submitSelector).catch(() => {
|
|
5173
|
+
});
|
|
5177
5174
|
try {
|
|
5178
|
-
await
|
|
5179
|
-
timeout:
|
|
5180
|
-
});
|
|
5181
|
-
await submitButton.click();
|
|
5182
|
-
} catch {
|
|
5183
|
-
const isDisabled = await submitButton.isDisabled().catch(() => true);
|
|
5184
|
-
if (!isDisabled) {
|
|
5185
|
-
await submitButton.click({ force: true }).catch(() => {
|
|
5175
|
+
if (await submitButton.isVisible({ timeout: 2e3 }).catch(() => false)) {
|
|
5176
|
+
await submitButton.click({ force: true, timeout: 2e3 }).catch(() => {
|
|
5186
5177
|
});
|
|
5187
|
-
} else {
|
|
5188
|
-
if (!page.url().includes("/auth") && await isPageLoggedIn(page, 2e3)) {
|
|
5189
|
-
return { success: true };
|
|
5190
|
-
}
|
|
5191
5178
|
}
|
|
5179
|
+
} catch {
|
|
5192
5180
|
}
|
|
5181
|
+
const passInput = page.locator(passwordSelector).first();
|
|
5182
|
+
await passInput.press("Enter", { timeout: 2e3 }).catch(() => {
|
|
5183
|
+
});
|
|
5193
5184
|
const postSubmitDeadline = Date.now() + 15e3;
|
|
5194
5185
|
while (Date.now() < postSubmitDeadline) {
|
|
5195
5186
|
if (page.isClosed()) break;
|
package/dist/reset-cooldowns.js
CHANGED
|
@@ -1174,6 +1174,9 @@ function unmarkAccountHeadersReady(accountId) {
|
|
|
1174
1174
|
if (!accountId) return;
|
|
1175
1175
|
headersReadyAccounts.delete(accountId);
|
|
1176
1176
|
}
|
|
1177
|
+
function isAccountHeadersReady(accountId) {
|
|
1178
|
+
return headersReadyAccounts.has(accountId);
|
|
1179
|
+
}
|
|
1177
1180
|
var cooldowns, headersReadyAccounts;
|
|
1178
1181
|
var init_account_manager = __esm({
|
|
1179
1182
|
"src/core/account-manager.ts"() {
|
|
@@ -3121,26 +3124,6 @@ async function isPageLoggedIn(page, timeoutMs = SESSION_PROBE_NAVIGATION_TIMEOUT
|
|
|
3121
3124
|
}
|
|
3122
3125
|
} catch {
|
|
3123
3126
|
}
|
|
3124
|
-
try {
|
|
3125
|
-
const refreshRes = await fetch("https://auth.qwen.ai/api/v2/auths/refresh", {
|
|
3126
|
-
method: "GET",
|
|
3127
|
-
credentials: "include",
|
|
3128
|
-
signal: AbortSignal.timeout(3e3)
|
|
3129
|
-
});
|
|
3130
|
-
if (refreshRes.status === 200) {
|
|
3131
|
-
const refreshJson = await refreshRes.json().catch(() => null);
|
|
3132
|
-
if (refreshJson && refreshJson.success === false) {
|
|
3133
|
-
const code = refreshJson.data?.code || refreshJson.code;
|
|
3134
|
-
const details = String(refreshJson.data?.details || "");
|
|
3135
|
-
if (code === "Unauthorized" || details.includes("revogado") || details.includes("revoked")) {
|
|
3136
|
-
return false;
|
|
3137
|
-
}
|
|
3138
|
-
}
|
|
3139
|
-
} else if (refreshRes.status === 401 || refreshRes.status === 403) {
|
|
3140
|
-
return false;
|
|
3141
|
-
}
|
|
3142
|
-
} catch {
|
|
3143
|
-
}
|
|
3144
3127
|
return true;
|
|
3145
3128
|
} catch {
|
|
3146
3129
|
return false;
|
|
@@ -3946,7 +3929,7 @@ async function initPlaywrightForAccount(rawAccount, headless = config.playwright
|
|
|
3946
3929
|
`\u26A0\uFE0F [Playwright] Initial navigation check for ${maskEmail(account.email)}: ${err.message}`
|
|
3947
3930
|
);
|
|
3948
3931
|
}
|
|
3949
|
-
if (!options.skipHeaderCapture) {
|
|
3932
|
+
if (!options.skipHeaderCapture && !isAccountHeadersReady(account.id)) {
|
|
3950
3933
|
acctPage.__qwenChatHomeLoaded = true;
|
|
3951
3934
|
await captureQwenHeaders(account.id);
|
|
3952
3935
|
}
|
|
@@ -4156,7 +4139,7 @@ async function loginToQwen(accountId, email, password) {
|
|
|
4156
4139
|
);
|
|
4157
4140
|
markAccountRateLimited(
|
|
4158
4141
|
accountId,
|
|
4159
|
-
|
|
4142
|
+
300 * 1e3,
|
|
4160
4143
|
"AuthFailed: All login methods exhausted"
|
|
4161
4144
|
);
|
|
4162
4145
|
return false;
|
|
@@ -4433,38 +4416,49 @@ async function loginViaUi(page, email, password) {
|
|
|
4433
4416
|
try {
|
|
4434
4417
|
const emailEl = document.querySelector('input[name="email"], input[type="email"], input[type="text"]');
|
|
4435
4418
|
const passEl = document.querySelector('input[type="password"], input[name="password"]');
|
|
4436
|
-
|
|
4437
|
-
|
|
4438
|
-
|
|
4439
|
-
|
|
4440
|
-
|
|
4441
|
-
|
|
4442
|
-
|
|
4443
|
-
|
|
4444
|
-
|
|
4445
|
-
|
|
4419
|
+
const setReactValue = (el, val) => {
|
|
4420
|
+
const proto = Object.getPrototypeOf(el);
|
|
4421
|
+
const setter = Object.getOwnPropertyDescriptor(proto, "value")?.set;
|
|
4422
|
+
if (setter) {
|
|
4423
|
+
setter.call(el, val);
|
|
4424
|
+
} else {
|
|
4425
|
+
el.value = val;
|
|
4426
|
+
}
|
|
4427
|
+
el.dispatchEvent(new Event("input", { bubbles: true }));
|
|
4428
|
+
el.dispatchEvent(new Event("change", { bubbles: true }));
|
|
4429
|
+
el.dispatchEvent(new Event("blur", { bubbles: true }));
|
|
4430
|
+
};
|
|
4431
|
+
if (emailEl) setReactValue(emailEl, emailEl.value);
|
|
4432
|
+
if (passEl) setReactValue(passEl, passEl.value);
|
|
4446
4433
|
} catch {
|
|
4447
4434
|
}
|
|
4448
4435
|
}).catch(() => {
|
|
4449
4436
|
});
|
|
4437
|
+
await sleep2(500);
|
|
4450
4438
|
const submitSelector = 'button.qwenchat-auth-pc-submit-button, button[type="submit"], button:has-text("Log in"), button:has-text("Sign in"), button:has-text("Fazer login"), button:has-text("Entrar"), button:has-text("Iniciar sesi\xF3n"), button:has-text("\u767B\u5F55")';
|
|
4451
4439
|
const submitButton = page.locator(submitSelector).first();
|
|
4440
|
+
await page.evaluate((sel) => {
|
|
4441
|
+
try {
|
|
4442
|
+
const btn = document.querySelector(sel);
|
|
4443
|
+
if (btn) {
|
|
4444
|
+
btn.removeAttribute("disabled");
|
|
4445
|
+
btn.classList.remove("ant-btn-disabled");
|
|
4446
|
+
btn.click();
|
|
4447
|
+
}
|
|
4448
|
+
} catch {
|
|
4449
|
+
}
|
|
4450
|
+
}, submitSelector).catch(() => {
|
|
4451
|
+
});
|
|
4452
4452
|
try {
|
|
4453
|
-
await
|
|
4454
|
-
timeout:
|
|
4455
|
-
});
|
|
4456
|
-
await submitButton.click();
|
|
4457
|
-
} catch {
|
|
4458
|
-
const isDisabled = await submitButton.isDisabled().catch(() => true);
|
|
4459
|
-
if (!isDisabled) {
|
|
4460
|
-
await submitButton.click({ force: true }).catch(() => {
|
|
4453
|
+
if (await submitButton.isVisible({ timeout: 2e3 }).catch(() => false)) {
|
|
4454
|
+
await submitButton.click({ force: true, timeout: 2e3 }).catch(() => {
|
|
4461
4455
|
});
|
|
4462
|
-
} else {
|
|
4463
|
-
if (!page.url().includes("/auth") && await isPageLoggedIn(page, 2e3)) {
|
|
4464
|
-
return { success: true };
|
|
4465
|
-
}
|
|
4466
4456
|
}
|
|
4457
|
+
} catch {
|
|
4467
4458
|
}
|
|
4459
|
+
const passInput = page.locator(passwordSelector).first();
|
|
4460
|
+
await passInput.press("Enter", { timeout: 2e3 }).catch(() => {
|
|
4461
|
+
});
|
|
4468
4462
|
const postSubmitDeadline = Date.now() + 15e3;
|
|
4469
4463
|
while (Date.now() < postSubmitDeadline) {
|
|
4470
4464
|
if (page.isClosed()) break;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "qwenproxy-cli",
|
|
3
|
-
"version": "1.5.
|
|
3
|
+
"version": "1.5.1",
|
|
4
4
|
"description": "High-performance OpenAI & Anthropic compatible API gateway for Qwen with multi-account rotation, interactive TUI, and resilient tool calling.",
|
|
5
5
|
"main": "src/index.ts",
|
|
6
6
|
"bin": {
|
|
@@ -29,8 +29,8 @@
|
|
|
29
29
|
"reset": "npx tsx src/reset-cooldowns.ts",
|
|
30
30
|
"purge": "npx tsx src/delete-chats.ts",
|
|
31
31
|
"test": "npm run test:mock && npm run test:live",
|
|
32
|
-
"test:mock": "tsx --test --test-concurrency=1 --env-file=.env.test src/tests/account-concurrency.test.ts src/tests/concurrency-latest-wins.test.ts src/tests/mutex.test.ts src/tests/context-truncation.test.ts src/tests/streaming-thinking-summary.test.ts src/tests/tool-call-guard.test.ts src/tests/utils-logger-full.test.ts src/tests/logger-toolcall-errors.test.ts src/tests/logger-toolcall-disabled.test.ts src/tests/utils-json-full.test.ts src/tests/utils-session-guard-full.test.ts src/tests/core-logic-full.test.ts src/tests/core-logic-accounts.test.ts src/tests/chat-validation-full.test.ts src/tests/chat-logic-full.test.ts src/tests/account-stickiness.test.ts src/tests/accounts-security.test.ts src/tests/advanced.test.ts src/tests/auth-playwright.test.ts src/tests/auth-session-persistence.test.ts src/tests/captcha-solver.test.ts src/tests/cache-scan-regex.test.ts src/tests/chat-cleanup.test.ts src/tests/chat-cleanup-auto.test.ts src/tests/chromium-args.test.ts src/tests/config-current.test.ts src/tests/context-meter.test.ts src/tests/concurrency.test.ts src/tests/delta.test.ts src/tests/fingerprint.test.ts src/tests/human-behavior.test.ts src/tests/index.test.ts src/tests/json-recovery.test.ts src/tests/jsonFix.test.ts src/tests/memory-cache.test.ts src/tests/model-alias.test.ts src/tests/model-registry-thinking.test.ts src/tests/models-endpoint.test.ts src/tests/multimodal.test.ts src/tests/oss-upload.test.ts src/tests/parser.test.ts src/tests/personalization-flow.test.ts src/tests/personalization-required.test.ts src/tests/retry-policy.test.ts src/tests/rotation.test.ts src/tests/server-lifecycle.test.ts src/tests/session-keeper.test.ts src/tests/session-state.test.ts src/tests/single-account-mode.test.ts src/tests/stream-retry.test.ts src/tests/tool-calls-endpoint.test.ts src/tests/tool-instructions-cache.test.ts src/tests/session-id.test.ts src/tests/watchdog-ram.test.ts src/tests/tool-recovery-t1t2.test.ts src/tests/abort-silent-t4.test.ts src/tests/t3-close-tag-escape.test.ts src/tests/t5-tool-cap.test.ts src/tests/logger-redaction.test.ts src/tests/stall-timeout-retryable.test.ts src/tests/t6-missing-quote-array-close.test.ts src/tests/watchdog-rss.test.ts src/tests/logical-thread-batch.test.ts src/tests/chat-pool-style.test.ts src/tests/queue-wait-cap.test.ts src/tests/context-eviction-order.test.ts src/tests/chat-in-progress-settle.test.ts src/tests/stream-lock-leak.test.ts src/tests/stream-semaphore.test.ts src/tests/waf-isolation.test.ts src/tests/legacy-completions.test.ts src/tests/response-format.test.ts src/tests/cors-headers.test.ts src/tests/local-auth.test.ts src/tests/safe-remote-url.test.ts src/tests/stream-lock-terminal-cancel.test.ts src/tests/parser-truncated-tool-call.test.ts src/tests/metrics-prometheus.test.ts src/tests/context-death-cleanup.test.ts src/tests/personalization-direct-fetch.test.ts src/tests/completion-headers-parity.test.ts src/tests/parallel-escape-repro.test.ts src/tests/chat-in-progress-scale-cost.test.ts src/tests/temp-chat-mode.test.ts src/tests/profile-reset.test.ts src/tests/error-classification.test.ts src/tests/quota-cooldown.test.ts src/tests/account-ready-gate.test.ts src/tests/tool-cap-notice.test.ts src/tests/interaction-flows.test.ts src/tests/headers-gate-cooldown-fallback.test.ts src/tests/unwrapped-tool-calls.test.ts src/tests/anthropic.test.ts src/tests/sync-clients.test.ts src/tests/sync-top10-clients.test.ts src/tests/cli.test.ts src/tests/paths.test.ts src/tests/tool-manifest.test.ts src/tests/tui.test.ts src/tests/tui-settings.test.ts src/tests/personalization-deadline.test.ts src/tests/update-cli.test.ts src/tests/batch-accounts.test.ts src/tests/shared-browser.test.ts src/tests/auth-credential-classification.test.ts src/tests/responses-api.test.ts src/tests/media-generation.test.ts src/tests/infer-tool-name.test.ts src/tests/native-session-headers.test.ts src/tests/omni-thinking.test.ts src/tests/protect-stuck-mutex.test.ts src/tests/stream-error-notice.test.ts src/tests/parser-deep-coverage.test.ts src/tests/tui-logs-copy.test.ts src/tests/auth-jwt-and-validation.test.ts src/tests/tui-port-and-key-modal.test.ts",
|
|
33
|
-
"test:mock:fast": "tsx --test --test-concurrency=2 --env-file=.env.test src/tests/account-concurrency.test.ts src/tests/concurrency-latest-wins.test.ts src/tests/mutex.test.ts src/tests/context-truncation.test.ts src/tests/streaming-thinking-summary.test.ts src/tests/tool-call-guard.test.ts src/tests/utils-logger-full.test.ts src/tests/logger-toolcall-errors.test.ts src/tests/logger-toolcall-disabled.test.ts src/tests/utils-json-full.test.ts src/tests/utils-session-guard-full.test.ts src/tests/core-logic-full.test.ts src/tests/core-logic-accounts.test.ts src/tests/chat-validation-full.test.ts src/tests/chat-logic-full.test.ts src/tests/account-stickiness.test.ts src/tests/accounts-security.test.ts src/tests/advanced.test.ts src/tests/auth-playwright.test.ts src/tests/auth-session-persistence.test.ts src/tests/captcha-solver.test.ts src/tests/cache-scan-regex.test.ts src/tests/chat-cleanup.test.ts src/tests/chat-cleanup-auto.test.ts src/tests/chromium-args.test.ts src/tests/config-current.test.ts src/tests/context-meter.test.ts src/tests/concurrency.test.ts src/tests/delta.test.ts src/tests/fingerprint.test.ts src/tests/human-behavior.test.ts src/tests/index.test.ts src/tests/json-recovery.test.ts src/tests/jsonFix.test.ts src/tests/memory-cache.test.ts src/tests/model-alias.test.ts src/tests/model-registry-thinking.test.ts src/tests/models-endpoint.test.ts src/tests/multimodal.test.ts src/tests/oss-upload.test.ts src/tests/parser.test.ts src/tests/personalization-flow.test.ts src/tests/personalization-required.test.ts src/tests/retry-policy.test.ts src/tests/rotation.test.ts src/tests/server-lifecycle.test.ts src/tests/session-keeper.test.ts src/tests/session-state.test.ts src/tests/single-account-mode.test.ts src/tests/stream-retry.test.ts src/tests/tool-calls-endpoint.test.ts src/tests/tool-instructions-cache.test.ts src/tests/session-id.test.ts src/tests/watchdog-ram.test.ts src/tests/tool-recovery-t1t2.test.ts src/tests/abort-silent-t4.test.ts src/tests/t3-close-tag-escape.test.ts src/tests/t5-tool-cap.test.ts src/tests/logger-redaction.test.ts src/tests/stall-timeout-retryable.test.ts src/tests/t6-missing-quote-array-close.test.ts src/tests/watchdog-rss.test.ts src/tests/logical-thread-batch.test.ts src/tests/chat-pool-style.test.ts src/tests/queue-wait-cap.test.ts src/tests/context-eviction-order.test.ts src/tests/chat-in-progress-settle.test.ts src/tests/stream-lock-leak.test.ts src/tests/stream-semaphore.test.ts src/tests/waf-isolation.test.ts src/tests/legacy-completions.test.ts src/tests/response-format.test.ts src/tests/cors-headers.test.ts src/tests/local-auth.test.ts src/tests/safe-remote-url.test.ts src/tests/stream-lock-terminal-cancel.test.ts src/tests/parser-truncated-tool-call.test.ts src/tests/metrics-prometheus.test.ts src/tests/context-death-cleanup.test.ts src/tests/personalization-direct-fetch.test.ts src/tests/completion-headers-parity.test.ts src/tests/parallel-escape-repro.test.ts src/tests/chat-in-progress-scale-cost.test.ts src/tests/temp-chat-mode.test.ts src/tests/profile-reset.test.ts src/tests/error-classification.test.ts src/tests/quota-cooldown.test.ts src/tests/account-ready-gate.test.ts src/tests/tool-cap-notice.test.ts src/tests/interaction-flows.test.ts src/tests/headers-gate-cooldown-fallback.test.ts src/tests/unwrapped-tool-calls.test.ts src/tests/anthropic.test.ts src/tests/sync-clients.test.ts src/tests/sync-top10-clients.test.ts src/tests/cli.test.ts src/tests/paths.test.ts src/tests/tool-manifest.test.ts src/tests/tui.test.ts src/tests/tui-settings.test.ts src/tests/personalization-deadline.test.ts src/tests/update-cli.test.ts src/tests/batch-accounts.test.ts src/tests/shared-browser.test.ts src/tests/auth-credential-classification.test.ts src/tests/responses-api.test.ts src/tests/media-generation.test.ts src/tests/infer-tool-name.test.ts src/tests/native-session-headers.test.ts src/tests/omni-thinking.test.ts src/tests/protect-stuck-mutex.test.ts src/tests/stream-error-notice.test.ts src/tests/parser-deep-coverage.test.ts src/tests/tui-logs-copy.test.ts src/tests/auth-jwt-and-validation.test.ts src/tests/tui-port-and-key-modal.test.ts",
|
|
32
|
+
"test:mock": "tsx --test --test-concurrency=1 --env-file=.env.test src/tests/account-concurrency.test.ts src/tests/concurrency-latest-wins.test.ts src/tests/mutex.test.ts src/tests/context-truncation.test.ts src/tests/streaming-thinking-summary.test.ts src/tests/tool-call-guard.test.ts src/tests/utils-logger-full.test.ts src/tests/logger-toolcall-errors.test.ts src/tests/logger-toolcall-disabled.test.ts src/tests/utils-json-full.test.ts src/tests/utils-session-guard-full.test.ts src/tests/core-logic-full.test.ts src/tests/core-logic-accounts.test.ts src/tests/chat-validation-full.test.ts src/tests/chat-logic-full.test.ts src/tests/account-stickiness.test.ts src/tests/accounts-security.test.ts src/tests/advanced.test.ts src/tests/auth-playwright.test.ts src/tests/auth-session-persistence.test.ts src/tests/captcha-solver.test.ts src/tests/cache-scan-regex.test.ts src/tests/chat-cleanup.test.ts src/tests/chat-cleanup-auto.test.ts src/tests/chromium-args.test.ts src/tests/config-current.test.ts src/tests/context-meter.test.ts src/tests/concurrency.test.ts src/tests/delta.test.ts src/tests/fingerprint.test.ts src/tests/human-behavior.test.ts src/tests/index.test.ts src/tests/json-recovery.test.ts src/tests/jsonFix.test.ts src/tests/memory-cache.test.ts src/tests/model-alias.test.ts src/tests/model-registry-thinking.test.ts src/tests/models-endpoint.test.ts src/tests/multimodal.test.ts src/tests/oss-upload.test.ts src/tests/parser.test.ts src/tests/personalization-flow.test.ts src/tests/personalization-required.test.ts src/tests/retry-policy.test.ts src/tests/rotation.test.ts src/tests/server-lifecycle.test.ts src/tests/session-keeper.test.ts src/tests/session-state.test.ts src/tests/single-account-mode.test.ts src/tests/stream-retry.test.ts src/tests/tool-calls-endpoint.test.ts src/tests/tool-instructions-cache.test.ts src/tests/session-id.test.ts src/tests/watchdog-ram.test.ts src/tests/tool-recovery-t1t2.test.ts src/tests/abort-silent-t4.test.ts src/tests/t3-close-tag-escape.test.ts src/tests/t5-tool-cap.test.ts src/tests/logger-redaction.test.ts src/tests/stall-timeout-retryable.test.ts src/tests/t6-missing-quote-array-close.test.ts src/tests/watchdog-rss.test.ts src/tests/logical-thread-batch.test.ts src/tests/chat-pool-style.test.ts src/tests/queue-wait-cap.test.ts src/tests/context-eviction-order.test.ts src/tests/chat-in-progress-settle.test.ts src/tests/stream-lock-leak.test.ts src/tests/stream-semaphore.test.ts src/tests/waf-isolation.test.ts src/tests/legacy-completions.test.ts src/tests/response-format.test.ts src/tests/cors-headers.test.ts src/tests/local-auth.test.ts src/tests/safe-remote-url.test.ts src/tests/stream-lock-terminal-cancel.test.ts src/tests/parser-truncated-tool-call.test.ts src/tests/metrics-prometheus.test.ts src/tests/context-death-cleanup.test.ts src/tests/personalization-direct-fetch.test.ts src/tests/completion-headers-parity.test.ts src/tests/parallel-escape-repro.test.ts src/tests/chat-in-progress-scale-cost.test.ts src/tests/temp-chat-mode.test.ts src/tests/profile-reset.test.ts src/tests/error-classification.test.ts src/tests/quota-cooldown.test.ts src/tests/account-ready-gate.test.ts src/tests/tool-cap-notice.test.ts src/tests/interaction-flows.test.ts src/tests/headers-gate-cooldown-fallback.test.ts src/tests/unwrapped-tool-calls.test.ts src/tests/anthropic.test.ts src/tests/sync-clients.test.ts src/tests/sync-top10-clients.test.ts src/tests/cli.test.ts src/tests/paths.test.ts src/tests/tool-manifest.test.ts src/tests/tui.test.ts src/tests/tui-settings.test.ts src/tests/personalization-deadline.test.ts src/tests/update-cli.test.ts src/tests/batch-accounts.test.ts src/tests/shared-browser.test.ts src/tests/auth-credential-classification.test.ts src/tests/responses-api.test.ts src/tests/media-generation.test.ts src/tests/infer-tool-name.test.ts src/tests/native-session-headers.test.ts src/tests/omni-thinking.test.ts src/tests/protect-stuck-mutex.test.ts src/tests/stream-error-notice.test.ts src/tests/parser-deep-coverage.test.ts src/tests/tui-logs-copy.test.ts src/tests/auth-jwt-and-validation.test.ts src/tests/tui-port-and-key-modal.test.ts src/tests/verify-zero-fake-cooldown.test.ts",
|
|
33
|
+
"test:mock:fast": "tsx --test --test-concurrency=2 --env-file=.env.test src/tests/account-concurrency.test.ts src/tests/concurrency-latest-wins.test.ts src/tests/mutex.test.ts src/tests/context-truncation.test.ts src/tests/streaming-thinking-summary.test.ts src/tests/tool-call-guard.test.ts src/tests/utils-logger-full.test.ts src/tests/logger-toolcall-errors.test.ts src/tests/logger-toolcall-disabled.test.ts src/tests/utils-json-full.test.ts src/tests/utils-session-guard-full.test.ts src/tests/core-logic-full.test.ts src/tests/core-logic-accounts.test.ts src/tests/chat-validation-full.test.ts src/tests/chat-logic-full.test.ts src/tests/account-stickiness.test.ts src/tests/accounts-security.test.ts src/tests/advanced.test.ts src/tests/auth-playwright.test.ts src/tests/auth-session-persistence.test.ts src/tests/captcha-solver.test.ts src/tests/cache-scan-regex.test.ts src/tests/chat-cleanup.test.ts src/tests/chat-cleanup-auto.test.ts src/tests/chromium-args.test.ts src/tests/config-current.test.ts src/tests/context-meter.test.ts src/tests/concurrency.test.ts src/tests/delta.test.ts src/tests/fingerprint.test.ts src/tests/human-behavior.test.ts src/tests/index.test.ts src/tests/json-recovery.test.ts src/tests/jsonFix.test.ts src/tests/memory-cache.test.ts src/tests/model-alias.test.ts src/tests/model-registry-thinking.test.ts src/tests/models-endpoint.test.ts src/tests/multimodal.test.ts src/tests/oss-upload.test.ts src/tests/parser.test.ts src/tests/personalization-flow.test.ts src/tests/personalization-required.test.ts src/tests/retry-policy.test.ts src/tests/rotation.test.ts src/tests/server-lifecycle.test.ts src/tests/session-keeper.test.ts src/tests/session-state.test.ts src/tests/single-account-mode.test.ts src/tests/stream-retry.test.ts src/tests/tool-calls-endpoint.test.ts src/tests/tool-instructions-cache.test.ts src/tests/session-id.test.ts src/tests/watchdog-ram.test.ts src/tests/tool-recovery-t1t2.test.ts src/tests/abort-silent-t4.test.ts src/tests/t3-close-tag-escape.test.ts src/tests/t5-tool-cap.test.ts src/tests/logger-redaction.test.ts src/tests/stall-timeout-retryable.test.ts src/tests/t6-missing-quote-array-close.test.ts src/tests/watchdog-rss.test.ts src/tests/logical-thread-batch.test.ts src/tests/chat-pool-style.test.ts src/tests/queue-wait-cap.test.ts src/tests/context-eviction-order.test.ts src/tests/chat-in-progress-settle.test.ts src/tests/stream-lock-leak.test.ts src/tests/stream-semaphore.test.ts src/tests/waf-isolation.test.ts src/tests/legacy-completions.test.ts src/tests/response-format.test.ts src/tests/cors-headers.test.ts src/tests/local-auth.test.ts src/tests/safe-remote-url.test.ts src/tests/stream-lock-terminal-cancel.test.ts src/tests/parser-truncated-tool-call.test.ts src/tests/metrics-prometheus.test.ts src/tests/context-death-cleanup.test.ts src/tests/personalization-direct-fetch.test.ts src/tests/completion-headers-parity.test.ts src/tests/parallel-escape-repro.test.ts src/tests/chat-in-progress-scale-cost.test.ts src/tests/temp-chat-mode.test.ts src/tests/profile-reset.test.ts src/tests/error-classification.test.ts src/tests/quota-cooldown.test.ts src/tests/account-ready-gate.test.ts src/tests/tool-cap-notice.test.ts src/tests/interaction-flows.test.ts src/tests/headers-gate-cooldown-fallback.test.ts src/tests/unwrapped-tool-calls.test.ts src/tests/anthropic.test.ts src/tests/sync-clients.test.ts src/tests/sync-top10-clients.test.ts src/tests/cli.test.ts src/tests/paths.test.ts src/tests/tool-manifest.test.ts src/tests/tui.test.ts src/tests/tui-settings.test.ts src/tests/personalization-deadline.test.ts src/tests/update-cli.test.ts src/tests/batch-accounts.test.ts src/tests/shared-browser.test.ts src/tests/auth-credential-classification.test.ts src/tests/responses-api.test.ts src/tests/media-generation.test.ts src/tests/infer-tool-name.test.ts src/tests/native-session-headers.test.ts src/tests/omni-thinking.test.ts src/tests/protect-stuck-mutex.test.ts src/tests/stream-error-notice.test.ts src/tests/parser-deep-coverage.test.ts src/tests/tui-logs-copy.test.ts src/tests/auth-jwt-and-validation.test.ts src/tests/tui-port-and-key-modal.test.ts src/tests/verify-zero-fake-cooldown.test.ts",
|
|
34
34
|
"test:live": "tsx --test src/tests/concurrentChat.test.ts src/tests/agenticStress.test.ts",
|
|
35
35
|
"test:stress": "npx tsx src/benchmarks/stress-test.ts",
|
|
36
36
|
"test:stress:personalization": "npx tsx src/benchmarks/stress-test-personalization.ts",
|
package/src/core/version.ts
CHANGED
|
@@ -2075,7 +2075,7 @@ export async function processStreamingResponse(
|
|
|
2075
2075
|
if (!toolParser) break;
|
|
2076
2076
|
const allToolsFailed =
|
|
2077
2077
|
toolParser.getMalformedToolCalls().length > 0 &&
|
|
2078
|
-
toolParser.
|
|
2078
|
+
toolParser.getSuccessfulToolCallCount() === 0;
|
|
2079
2079
|
if (!allToolsFailed) break;
|
|
2080
2080
|
|
|
2081
2081
|
const malformedCalls = toolParser.getMalformedToolCalls();
|
|
@@ -66,6 +66,7 @@ import { Mutex } from "../core/mutex.ts";
|
|
|
66
66
|
import {
|
|
67
67
|
markAccountHeadersReady,
|
|
68
68
|
unmarkAccountHeadersReady,
|
|
69
|
+
isAccountHeadersReady,
|
|
69
70
|
markAccountRateLimited,
|
|
70
71
|
} from "../core/account-manager.ts";
|
|
71
72
|
import { getAccountsByPriority } from "../core/account-priority.ts";
|
|
@@ -408,27 +409,6 @@ export async function isPageLoggedIn(
|
|
|
408
409
|
}
|
|
409
410
|
} catch {}
|
|
410
411
|
|
|
411
|
-
// Fallback cross-origin refresh probe
|
|
412
|
-
try {
|
|
413
|
-
const refreshRes = await fetch("https://auth.qwen.ai/api/v2/auths/refresh", {
|
|
414
|
-
method: "GET",
|
|
415
|
-
credentials: "include",
|
|
416
|
-
signal: AbortSignal.timeout(3000),
|
|
417
|
-
});
|
|
418
|
-
if (refreshRes.status === 200) {
|
|
419
|
-
const refreshJson: any = await refreshRes.json().catch(() => null);
|
|
420
|
-
if (refreshJson && refreshJson.success === false) {
|
|
421
|
-
const code = refreshJson.data?.code || refreshJson.code;
|
|
422
|
-
const details = String(refreshJson.data?.details || "");
|
|
423
|
-
if (code === "Unauthorized" || details.includes("revogado") || details.includes("revoked")) {
|
|
424
|
-
return false;
|
|
425
|
-
}
|
|
426
|
-
}
|
|
427
|
-
} else if (refreshRes.status === 401 || refreshRes.status === 403) {
|
|
428
|
-
return false;
|
|
429
|
-
}
|
|
430
|
-
} catch {}
|
|
431
|
-
|
|
432
412
|
// If an authenticated user object is confirmed with a real user identity,
|
|
433
413
|
// the session is 100% valid and verified by upstream.
|
|
434
414
|
return true;
|
|
@@ -1811,7 +1791,7 @@ export async function initPlaywrightForAccount(
|
|
|
1811
1791
|
);
|
|
1812
1792
|
}
|
|
1813
1793
|
|
|
1814
|
-
if (!options.skipHeaderCapture) {
|
|
1794
|
+
if (!options.skipHeaderCapture && !isAccountHeadersReady(account.id)) {
|
|
1815
1795
|
(acctPage as any).__qwenChatHomeLoaded = true;
|
|
1816
1796
|
await captureQwenHeaders(account.id);
|
|
1817
1797
|
}
|
|
@@ -2224,7 +2204,7 @@ async function loginToQwen(
|
|
|
2224
2204
|
);
|
|
2225
2205
|
markAccountRateLimited(
|
|
2226
2206
|
accountId,
|
|
2227
|
-
|
|
2207
|
+
300 * 1000,
|
|
2228
2208
|
"AuthFailed: All login methods exhausted",
|
|
2229
2209
|
);
|
|
2230
2210
|
return false;
|
|
@@ -2582,41 +2562,52 @@ async function loginViaUi(
|
|
|
2582
2562
|
await page
|
|
2583
2563
|
.evaluate(() => {
|
|
2584
2564
|
try {
|
|
2585
|
-
const emailEl = document.querySelector('input[name="email"], input[type="email"], input[type="text"]');
|
|
2586
|
-
const passEl = document.querySelector('input[type="password"], input[name="password"]');
|
|
2587
|
-
|
|
2588
|
-
|
|
2589
|
-
|
|
2590
|
-
|
|
2591
|
-
|
|
2592
|
-
|
|
2593
|
-
|
|
2594
|
-
|
|
2595
|
-
|
|
2596
|
-
|
|
2565
|
+
const emailEl = document.querySelector('input[name="email"], input[type="email"], input[type="text"]') as HTMLInputElement | null;
|
|
2566
|
+
const passEl = document.querySelector('input[type="password"], input[name="password"]') as HTMLInputElement | null;
|
|
2567
|
+
const setReactValue = (el: HTMLInputElement, val: string) => {
|
|
2568
|
+
const proto = Object.getPrototypeOf(el);
|
|
2569
|
+
const setter = Object.getOwnPropertyDescriptor(proto, "value")?.set;
|
|
2570
|
+
if (setter) {
|
|
2571
|
+
setter.call(el, val);
|
|
2572
|
+
} else {
|
|
2573
|
+
el.value = val;
|
|
2574
|
+
}
|
|
2575
|
+
el.dispatchEvent(new Event("input", { bubbles: true }));
|
|
2576
|
+
el.dispatchEvent(new Event("change", { bubbles: true }));
|
|
2577
|
+
el.dispatchEvent(new Event("blur", { bubbles: true }));
|
|
2578
|
+
};
|
|
2579
|
+
if (emailEl) setReactValue(emailEl, emailEl.value);
|
|
2580
|
+
if (passEl) setReactValue(passEl, passEl.value);
|
|
2597
2581
|
} catch {}
|
|
2598
2582
|
})
|
|
2599
2583
|
.catch(() => {});
|
|
2600
2584
|
|
|
2601
|
-
|
|
2585
|
+
await sleep(500);
|
|
2586
|
+
|
|
2587
|
+
// Multi-modal submit: DOM click, Playwright force click, and Enter press
|
|
2602
2588
|
const submitSelector =
|
|
2603
2589
|
'button.qwenchat-auth-pc-submit-button, button[type="submit"], button:has-text("Log in"), button:has-text("Sign in"), button:has-text("Fazer login"), button:has-text("Entrar"), button:has-text("Iniciar sesión"), button:has-text("登录")';
|
|
2604
2590
|
const submitButton = page.locator(submitSelector).first();
|
|
2605
|
-
|
|
2606
|
-
|
|
2607
|
-
|
|
2608
|
-
|
|
2609
|
-
|
|
2610
|
-
|
|
2611
|
-
|
|
2612
|
-
|
|
2613
|
-
await submitButton.click({ force: true }).catch(() => {});
|
|
2614
|
-
} else {
|
|
2615
|
-
if (!page.url().includes("/auth") && (await isPageLoggedIn(page, 2000))) {
|
|
2616
|
-
return { success: true };
|
|
2591
|
+
|
|
2592
|
+
await page.evaluate((sel) => {
|
|
2593
|
+
try {
|
|
2594
|
+
const btn = document.querySelector(sel) as HTMLButtonElement | null;
|
|
2595
|
+
if (btn) {
|
|
2596
|
+
btn.removeAttribute("disabled");
|
|
2597
|
+
btn.classList.remove("ant-btn-disabled");
|
|
2598
|
+
btn.click();
|
|
2617
2599
|
}
|
|
2600
|
+
} catch {}
|
|
2601
|
+
}, submitSelector).catch(() => {});
|
|
2602
|
+
|
|
2603
|
+
try {
|
|
2604
|
+
if (await submitButton.isVisible({ timeout: 2000 }).catch(() => false)) {
|
|
2605
|
+
await submitButton.click({ force: true, timeout: 2000 }).catch(() => {});
|
|
2618
2606
|
}
|
|
2619
|
-
}
|
|
2607
|
+
} catch {}
|
|
2608
|
+
|
|
2609
|
+
const passInput = page.locator(passwordSelector).first();
|
|
2610
|
+
await passInput.press("Enter", { timeout: 2000 }).catch(() => {});
|
|
2620
2611
|
// Post-submit verification loop: wait up to 15s for captcha, redirect, cookies, or errors
|
|
2621
2612
|
const postSubmitDeadline = Date.now() + 15_000;
|
|
2622
2613
|
while (Date.now() < postSubmitDeadline) {
|
|
@@ -81,12 +81,27 @@ export function buildToolInstructions(
|
|
|
81
81
|
? Object.keys(properties)[0]
|
|
82
82
|
: null;
|
|
83
83
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
84
|
+
const sampleArgs1 = firstPropKey
|
|
85
|
+
? JSON.stringify({ [firstPropKey]: "value1" })
|
|
86
|
+
: "{}";
|
|
87
|
+
|
|
88
|
+
// Pick second tool dynamically when available to teach tool diversity
|
|
89
|
+
const secondTool = toolList.length > 1 ? toolList[1] : null;
|
|
90
|
+
const secondToolName = secondTool
|
|
91
|
+
? (typeof secondTool?.function?.name === "string" && secondTool.function.name) ||
|
|
92
|
+
(typeof secondTool?.name === "string" && secondTool.name) ||
|
|
93
|
+
sampleToolName
|
|
94
|
+
: null;
|
|
95
|
+
const secondProperties =
|
|
96
|
+
secondTool?.function?.parameters?.properties ||
|
|
97
|
+
secondTool?.parameters?.properties;
|
|
98
|
+
const secondPropKey =
|
|
99
|
+
secondProperties && typeof secondProperties === "object"
|
|
100
|
+
? Object.keys(secondProperties)[0]
|
|
101
|
+
: null;
|
|
102
|
+
const sampleArgs2 = secondPropKey
|
|
103
|
+
? JSON.stringify({ [secondPropKey]: "value2" })
|
|
104
|
+
: "{}";
|
|
90
105
|
|
|
91
106
|
let forcedInstruction = "";
|
|
92
107
|
if (
|
|
@@ -103,32 +118,30 @@ export function buildToolInstructions(
|
|
|
103
118
|
forcedInstruction = `\nCRITICAL: You MUST call at least one tool from the list above in this response.\n`;
|
|
104
119
|
}
|
|
105
120
|
|
|
121
|
+
const secondExample = secondToolName
|
|
122
|
+
? `\n${TOOL_CALL_OPEN}\n{"name": "${secondToolName}", "arguments": ${sampleArgs2}}\n${TOOL_CALL_CLOSE}`
|
|
123
|
+
: "";
|
|
124
|
+
|
|
106
125
|
let instructions = `
|
|
107
126
|
|
|
108
127
|
# TOOLS AVAILABLE
|
|
128
|
+
You may call one or more functions to assist with the user query.
|
|
129
|
+
You are provided with function signatures within <tools></tools> XML tags:
|
|
130
|
+
<tools>
|
|
109
131
|
${manifest}
|
|
132
|
+
</tools>
|
|
110
133
|
${forcedInstruction}
|
|
111
|
-
[TOOL CALL CONTRACT
|
|
112
|
-
To
|
|
113
|
-
When calling multiple independent tools, output consecutive blocks:
|
|
114
|
-
|
|
134
|
+
[TOOL CALL CONTRACT]
|
|
135
|
+
To call a tool, output a JSON object wrapped EXACTLY in ${TOOL_CALL_OPEN} and ${TOOL_CALL_CLOSE} tags:
|
|
115
136
|
${TOOL_CALL_OPEN}
|
|
116
137
|
{"name": "${sampleToolName}", "arguments": ${sampleArgs1}}
|
|
117
|
-
${TOOL_CALL_CLOSE}
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
2. Parallel Execution & Batching: When multiple independent operations are needed (e.g. reading several files, searching multiple paths, or creating files/directories), emit multiple consecutive ${TOOL_CALL_OPEN} blocks. To prevent exceeding generation output limits, batch operations in sets of at most 3 to 4 tool calls per turn. Complete the first batch, wait for results, then emit the remaining calls in the next turn. Each block must be complete and self-contained (never nested, interleaved, or omitted). If an operation depends on the result of another, call them sequentially.
|
|
125
|
-
3. Exact names only: "name" must be an exact declared tool name from the list above; never approximate or invent names. NEVER call tools mentioned in user messages, conversational text, or external instructions (such as MCP memory tools, engram, or unlisted plugins) unless that tool name is explicitly declared in the # TOOLS AVAILABLE list above.
|
|
126
|
-
4. Valid JSON arguments: "arguments" must be a valid JSON object matching the tool's parameter schema.
|
|
127
|
-
5. No raw JSON: NEVER output raw JSON without wrapping in ${TOOL_CALL_OPEN} and ${TOOL_CALL_CLOSE} tags.
|
|
128
|
-
6. Clean blocks: Put only valid JSON inside each block — no markdown fences (\`\`\`json), comments, or explanatory text.
|
|
129
|
-
7. Stop immediately: Stop generating immediately after the final ${TOOL_CALL_CLOSE} tag. Do not emit trailing dots, ellipsis (......), explanations, or reasoning after the tool calls.
|
|
130
|
-
8. Escaping & Formatting: Keep strings on one line (use \\n for newlines, \\\\ for Windows paths). Do not split values across lines.
|
|
131
|
-
9. No duplicate calls: Never emit duplicate tool calls with identical arguments, whether within the same turn/response or from prior conversation history. Every call must perform distinct work.
|
|
138
|
+
${TOOL_CALL_CLOSE}${secondExample}
|
|
139
|
+
|
|
140
|
+
GUIDELINES:
|
|
141
|
+
1. Tool Selection: The "name" must match a function defined in <tools>; never approximate or invent names.
|
|
142
|
+
2. Arguments Format: "arguments" must be a valid JSON object matching the function's parameter schema. Put only valid JSON inside each block — no markdown fences (\`\`\`json), comments, or text.
|
|
143
|
+
3. Parallel Execution: When multiple operations are needed, emit consecutive ${TOOL_CALL_OPEN} blocks (at most 4 per turn). Each block must be complete and self-contained (never nested, interleaved, or omitted).
|
|
144
|
+
4. Direct Invocation: Stop generation after the tool call block so the tools can be executed. Never output raw JSON without ${TOOL_CALL_OPEN} tags.
|
|
132
145
|
`;
|
|
133
146
|
|
|
134
147
|
// Cache result (with LRU-style eviction)
|
package/src/tools/parser.ts
CHANGED
|
@@ -1275,6 +1275,7 @@ export class StreamingToolParser {
|
|
|
1275
1275
|
private currentOpenTag = TOOL_START_LITERAL;
|
|
1276
1276
|
private currentCloseTag = TOOL_END;
|
|
1277
1277
|
private emittedToolCallCount = 0;
|
|
1278
|
+
private successfulToolCallsCount = 0;
|
|
1278
1279
|
private pendingLeadIn = "";
|
|
1279
1280
|
private tools: ToolDefinitionLike[] = [];
|
|
1280
1281
|
private declaredToolNames: string[] = [];
|
|
@@ -1568,6 +1569,7 @@ export class StreamingToolParser {
|
|
|
1568
1569
|
// or the arguments would be appended twice and corrupted.
|
|
1569
1570
|
this.flushPendingToolCallDeltas(result);
|
|
1570
1571
|
this.emittedToolCallCount++;
|
|
1572
|
+
this.successfulToolCallsCount++;
|
|
1571
1573
|
this.pendingLeadIn = "";
|
|
1572
1574
|
incremental.startEmitted = false;
|
|
1573
1575
|
incremental.disabled = true;
|
|
@@ -1577,6 +1579,7 @@ export class StreamingToolParser {
|
|
|
1577
1579
|
this.flushPendingToolCallDeltas(result);
|
|
1578
1580
|
result.toolCalls.push(tc);
|
|
1579
1581
|
this.emittedToolCallCount++;
|
|
1582
|
+
this.successfulToolCallsCount++;
|
|
1580
1583
|
this.pendingLeadIn = "";
|
|
1581
1584
|
}
|
|
1582
1585
|
|
|
@@ -2199,6 +2202,10 @@ export class StreamingToolParser {
|
|
|
2199
2202
|
return this.emittedToolCallCount;
|
|
2200
2203
|
}
|
|
2201
2204
|
|
|
2205
|
+
getSuccessfulToolCallCount(): number {
|
|
2206
|
+
return this.successfulToolCallsCount;
|
|
2207
|
+
}
|
|
2208
|
+
|
|
2202
2209
|
isInsideTool(): boolean {
|
|
2203
2210
|
return this.insideTool;
|
|
2204
2211
|
}
|