qwenproxy-cli 1.5.0 â 1.5.2
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 +40 -44
- package/dist/delete-chats.js +91 -44
- package/dist/index.js +87 -121
- package/dist/login.js +37 -44
- package/dist/reset-cooldowns.js +40 -44
- package/dist/sync-clients.js +2 -0
- package/package.json +3 -3
- package/src/api/server.ts +17 -59
- package/src/core/config.ts +2 -0
- package/src/core/version.ts +1 -1
- package/src/routes/chat/streaming.ts +1 -1
- package/src/services/auth-playwright.ts +5 -0
- package/src/services/playwright.ts +40 -49
- package/src/tools/instructions.ts +38 -25
- package/src/tools/parser.ts +7 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "qwenproxy-cli",
|
|
3
|
-
"version": "1.5.
|
|
3
|
+
"version": "1.5.2",
|
|
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/api/server.ts
CHANGED
|
@@ -877,23 +877,20 @@ export async function startServer(options?: {
|
|
|
877
877
|
);
|
|
878
878
|
});
|
|
879
879
|
} else if (remainingAccounts.length > 0) {
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
);
|
|
883
|
-
|
|
884
|
-
// In background: warm 1 reserve account (if maxActiveContexts > 1) and
|
|
885
|
-
// validate the rest of the standby accounts
|
|
880
|
+
// In background: warm reserve accounts up to a safe limit (default 4 total ready accounts)
|
|
881
|
+
// to prevent IP-level login flood / anti-bot challenge rate-limits on Alibaba.
|
|
886
882
|
void (async () => {
|
|
887
|
-
const { validateAccountLogin } = await import("../services/playwright.ts");
|
|
888
883
|
const { ensureAccountInPriority } = await import("../core/account-priority.ts");
|
|
889
884
|
|
|
890
|
-
|
|
885
|
+
const targetWarmTotal = Math.min(
|
|
886
|
+
config.playwright.startupWarmAccounts,
|
|
887
|
+
totalAccounts,
|
|
888
|
+
);
|
|
889
|
+
let currentReadyCount = readyAccountIds.size;
|
|
891
890
|
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
for (; reserveCandidateIdx < remainingAccounts.length; reserveCandidateIdx++) {
|
|
896
|
-
const reserveAccount = remainingAccounts[reserveCandidateIdx];
|
|
891
|
+
if (currentReadyCount < targetWarmTotal && remainingAccounts.length > 0) {
|
|
892
|
+
for (const reserveAccount of remainingAccounts) {
|
|
893
|
+
if (currentReadyCount >= targetWarmTotal) break;
|
|
897
894
|
try {
|
|
898
895
|
const ok = await prepareAccountRuntime(
|
|
899
896
|
reserveAccount,
|
|
@@ -903,12 +900,12 @@ export async function startServer(options?: {
|
|
|
903
900
|
warmQwenChatPool,
|
|
904
901
|
);
|
|
905
902
|
if (ok) {
|
|
903
|
+
readyAccountIds.add(reserveAccount.id);
|
|
906
904
|
ensureAccountInPriority(reserveAccount.id);
|
|
905
|
+
currentReadyCount++;
|
|
907
906
|
console.log(
|
|
908
|
-
`â
[Server] Reserve account ready (
|
|
907
|
+
`â
[Server] Reserve account ready (${currentReadyCount}/${totalAccounts}): ${maskEmail(reserveAccount.email)}`,
|
|
909
908
|
);
|
|
910
|
-
reserveCandidateIdx++;
|
|
911
|
-
break;
|
|
912
909
|
}
|
|
913
910
|
} catch (err) {
|
|
914
911
|
console.warn(
|
|
@@ -916,56 +913,17 @@ export async function startServer(options?: {
|
|
|
916
913
|
);
|
|
917
914
|
}
|
|
918
915
|
}
|
|
919
|
-
accountsToValidate = remainingAccounts.slice(reserveCandidateIdx);
|
|
920
916
|
}
|
|
921
917
|
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
for (const account of accountsToValidate) {
|
|
926
|
-
try {
|
|
927
|
-
const creds = getAccountCredentials(account.id) ?? account;
|
|
928
|
-
// Validate login in background with real unmasked credentials
|
|
929
|
-
const ok = await validateAccountLogin(
|
|
930
|
-
creds,
|
|
931
|
-
config.playwright.headless,
|
|
932
|
-
config.playwright.browser,
|
|
933
|
-
);
|
|
934
|
-
if (ok) {
|
|
935
|
-
// Add to priority list only once validated
|
|
936
|
-
ensureAccountInPriority(account.id);
|
|
937
|
-
validated++;
|
|
938
|
-
console.log(
|
|
939
|
-
`â
[Server] Standby account validated: ${maskEmail(account.email)}`,
|
|
940
|
-
);
|
|
941
|
-
} else {
|
|
942
|
-
failed++;
|
|
943
|
-
console.warn(
|
|
944
|
-
`â ïļ [Server] Standby account login failed: ${maskEmail(account.email)} (quarantined)`,
|
|
945
|
-
);
|
|
946
|
-
}
|
|
947
|
-
} catch (error) {
|
|
948
|
-
failed++;
|
|
949
|
-
console.warn(
|
|
950
|
-
`â ïļ [Server] Standby account validation error: ${maskEmail(account.email)}: ${getErrorMessage(error)} (quarantined)`,
|
|
951
|
-
);
|
|
952
|
-
const { markAccountRateLimited } = await import("../core/account-manager.ts");
|
|
953
|
-
markAccountRateLimited(
|
|
954
|
-
account.id,
|
|
955
|
-
24 * 3600 * 1000,
|
|
956
|
-
`StandbyValidationError: ${getErrorMessage(error)}`,
|
|
957
|
-
);
|
|
958
|
-
}
|
|
959
|
-
}
|
|
960
|
-
|
|
961
|
-
if (validated > 0 || failed > 0) {
|
|
918
|
+
const standbyCount = totalAccounts - currentReadyCount;
|
|
919
|
+
if (standbyCount > 0) {
|
|
962
920
|
console.log(
|
|
963
|
-
|
|
921
|
+
`ðŠķ [Server] ${standbyCount} standby account(s) ready in pool (will initialize on demand when active accounts rotate)`,
|
|
964
922
|
);
|
|
965
923
|
}
|
|
966
924
|
})().catch((error) => {
|
|
967
925
|
console.warn(
|
|
968
|
-
`â [Server] Background
|
|
926
|
+
`â [Server] Background account preparation failed: ${getErrorMessage(error)}`,
|
|
969
927
|
);
|
|
970
928
|
});
|
|
971
929
|
}
|
package/src/core/config.ts
CHANGED
|
@@ -76,6 +76,7 @@ const envSchema = z
|
|
|
76
76
|
// own context while serving. Accounts in cooldown (rate-limited) sit idle,
|
|
77
77
|
// drop out of the warm set and get evicted.
|
|
78
78
|
PLAYWRIGHT_MAX_ACTIVE_CONTEXTS: z.string().default("2"),
|
|
79
|
+
PLAYWRIGHT_STARTUP_WARM_ACCOUNTS: z.string().default("4"),
|
|
79
80
|
PLAYWRIGHT_PREPARE_ALL_ON_STARTUP: z.string().default("false"),
|
|
80
81
|
CAPTCHA_SOLVER_ENABLED: z.string().default("true"),
|
|
81
82
|
CAPTCHA_SOLVER_MAX_ATTEMPTS: z.string().default("3"),
|
|
@@ -235,6 +236,7 @@ export const config = {
|
|
|
235
236
|
jsHeapMb: Math.max(64, parseInt(env.PLAYWRIGHT_JS_HEAP_MB)),
|
|
236
237
|
lowMemoryFlags: env.PLAYWRIGHT_LOW_MEMORY_FLAGS !== "false",
|
|
237
238
|
maxActiveContexts: Math.max(0, parseInt(env.PLAYWRIGHT_MAX_ACTIVE_CONTEXTS)),
|
|
239
|
+
startupWarmAccounts: Math.max(1, parseInt(env.PLAYWRIGHT_STARTUP_WARM_ACCOUNTS)),
|
|
238
240
|
prepareAllOnStartup: env.PLAYWRIGHT_PREPARE_ALL_ON_STARTUP !== "false",
|
|
239
241
|
},
|
|
240
242
|
captcha: {
|
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();
|
|
@@ -56,6 +56,11 @@ async function ensurePlaywrightInitialized(accountId: string): Promise<void> {
|
|
|
56
56
|
} catch {
|
|
57
57
|
// Non-fatal: chat creation will still work with default account settings.
|
|
58
58
|
}
|
|
59
|
+
|
|
60
|
+
try {
|
|
61
|
+
const { ensureAccountInPriority } = await import("../core/account-priority.ts");
|
|
62
|
+
ensureAccountInPriority(accountId);
|
|
63
|
+
} catch {}
|
|
59
64
|
}
|
|
60
65
|
|
|
61
66
|
export async function getBasicHeaders(accountId?: string): Promise<{
|
|
@@ -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
|
}
|